1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/tests/test_udp_socket.h"
6
7#include <vector>
8
9#include "ppapi/cpp/pass_ref.h"
10#include "ppapi/cpp/tcp_socket.h"
11#include "ppapi/cpp/udp_socket.h"
12#include "ppapi/cpp/var.h"
13#include "ppapi/tests/test_utils.h"
14#include "ppapi/tests/testing_instance.h"
15
16REGISTER_TEST_CASE(UDPSocket);
17
18namespace {
19
20const uint16_t kPortScanFrom = 1024;
21const uint16_t kPortScanTo = 4096;
22
23pp::NetAddress ReplacePort(const pp::InstanceHandle& instance,
24                           const pp::NetAddress& addr,
25                           uint16_t port) {
26  switch (addr.GetFamily()) {
27    case PP_NETADDRESS_FAMILY_IPV4: {
28      PP_NetAddress_IPv4 ipv4_addr;
29      if (!addr.DescribeAsIPv4Address(&ipv4_addr))
30        break;
31      ipv4_addr.port = ConvertToNetEndian16(port);
32      return pp::NetAddress(instance, ipv4_addr);
33    }
34    case PP_NETADDRESS_FAMILY_IPV6: {
35      PP_NetAddress_IPv6 ipv6_addr;
36      if (!addr.DescribeAsIPv6Address(&ipv6_addr))
37        break;
38      ipv6_addr.port = ConvertToNetEndian16(port);
39      return pp::NetAddress(instance, ipv6_addr);
40    }
41    default: {
42      PP_NOTREACHED();
43    }
44  }
45  return pp::NetAddress();
46}
47
48}  // namespace
49
50TestUDPSocket::TestUDPSocket(TestingInstance* instance) : TestCase(instance) {
51}
52
53bool TestUDPSocket::Init() {
54  bool tcp_socket_is_available = pp::TCPSocket::IsAvailable();
55  if (!tcp_socket_is_available)
56    instance_->AppendError("PPB_TCPSocket interface not available");
57
58  bool udp_socket_is_available = pp::UDPSocket::IsAvailable();
59  if (!udp_socket_is_available)
60    instance_->AppendError("PPB_UDPSocket interface not available");
61
62  bool net_address_is_available = pp::NetAddress::IsAvailable();
63  if (!net_address_is_available)
64    instance_->AppendError("PPB_NetAddress interface not available");
65
66  std::string host;
67  uint16_t port = 0;
68  bool init_address =
69      GetLocalHostPort(instance_->pp_instance(), &host, &port) &&
70      ResolveHost(instance_->pp_instance(), host, port, &address_);
71  if (!init_address)
72    instance_->AppendError("Can't init address");
73
74  return tcp_socket_is_available &&
75      udp_socket_is_available &&
76      net_address_is_available &&
77      init_address &&
78      CheckTestingInterface() &&
79      EnsureRunningOverHTTP();
80}
81
82void TestUDPSocket::RunTests(const std::string& filter) {
83  RUN_CALLBACK_TEST(TestUDPSocket, ReadWrite, filter);
84  RUN_CALLBACK_TEST(TestUDPSocket, Broadcast, filter);
85  RUN_CALLBACK_TEST(TestUDPSocket, SetOption, filter);
86}
87
88std::string TestUDPSocket::GetLocalAddress(pp::NetAddress* address) {
89  pp::TCPSocket socket(instance_);
90  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
91  callback.WaitForResult(socket.Connect(address_, callback.GetCallback()));
92  CHECK_CALLBACK_BEHAVIOR(callback);
93  ASSERT_EQ(PP_OK, callback.result());
94  *address = socket.GetLocalAddress();
95  ASSERT_NE(0, address->pp_resource());
96  socket.Close();
97  PASS();
98}
99
100std::string TestUDPSocket::SetBroadcastOptions(pp::UDPSocket* socket) {
101  TestCompletionCallback callback_1(instance_->pp_instance(), callback_type());
102  callback_1.WaitForResult(socket->SetOption(
103      PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(true),
104      callback_1.GetCallback()));
105  CHECK_CALLBACK_BEHAVIOR(callback_1);
106  ASSERT_EQ(PP_OK, callback_1.result());
107
108  TestCompletionCallback callback_2(instance_->pp_instance(), callback_type());
109  callback_2.WaitForResult(socket->SetOption(
110      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback_2.GetCallback()));
111  CHECK_CALLBACK_BEHAVIOR(callback_2);
112  ASSERT_EQ(PP_OK, callback_2.result());
113
114  PASS();
115}
116
117std::string TestUDPSocket::BindUDPSocket(pp::UDPSocket* socket,
118                                         const pp::NetAddress& address) {
119  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
120  callback.WaitForResult(socket->Bind(address, callback.GetCallback()));
121  CHECK_CALLBACK_BEHAVIOR(callback);
122  ASSERT_EQ(PP_OK, callback.result());
123  PASS();
124}
125
126std::string TestUDPSocket::LookupPortAndBindUDPSocket(
127    pp::UDPSocket* socket,
128    pp::NetAddress* address) {
129  pp::NetAddress base_address;
130  ASSERT_SUBTEST_SUCCESS(GetLocalAddress(&base_address));
131
132  bool is_free_port_found = false;
133  for (uint16_t port = kPortScanFrom; port < kPortScanTo; ++port) {
134    pp::NetAddress new_address = ReplacePort(instance_, base_address, port);
135    ASSERT_NE(0, new_address.pp_resource());
136    if (BindUDPSocket(socket, new_address).empty()) {
137      is_free_port_found = true;
138      break;
139    }
140  }
141  if (!is_free_port_found)
142    return "Can't find available port";
143
144  *address = socket->GetBoundAddress();
145  ASSERT_NE(0, address->pp_resource());
146
147  PASS();
148}
149
150std::string TestUDPSocket::ReadSocket(pp::UDPSocket* socket,
151                                      pp::NetAddress* address,
152                                      size_t size,
153                                      std::string* message) {
154  std::vector<char> buffer(size);
155  TestCompletionCallbackWithOutput<pp::NetAddress> callback(
156      instance_->pp_instance(), callback_type());
157  callback.WaitForResult(
158      socket->RecvFrom(&buffer[0], size, callback.GetCallback()));
159  CHECK_CALLBACK_BEHAVIOR(callback);
160  ASSERT_FALSE(callback.result() < 0);
161  ASSERT_EQ(size, static_cast<size_t>(callback.result()));
162  *address = callback.output();
163  message->assign(buffer.begin(), buffer.end());
164  PASS();
165}
166
167std::string TestUDPSocket::PassMessage(pp::UDPSocket* target,
168                                       pp::UDPSocket* source,
169                                       const pp::NetAddress& target_address,
170                                       const std::string& message,
171                                       pp::NetAddress* recvfrom_address) {
172  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
173  int32_t rv = source->SendTo(message.c_str(), message.size(),
174                              target_address,
175                              callback.GetCallback());
176  std::string str;
177  ASSERT_SUBTEST_SUCCESS(ReadSocket(target, recvfrom_address, message.size(),
178                                    &str));
179
180  callback.WaitForResult(rv);
181  CHECK_CALLBACK_BEHAVIOR(callback);
182  ASSERT_FALSE(callback.result() < 0);
183  ASSERT_EQ(message.size(), static_cast<size_t>(callback.result()));
184  ASSERT_EQ(message, str);
185  PASS();
186}
187
188std::string TestUDPSocket::TestReadWrite() {
189  pp::UDPSocket server_socket(instance_), client_socket(instance_);
190  pp::NetAddress server_address, client_address;
191
192  ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&server_socket,
193                                                    &server_address));
194  ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&client_socket,
195                                                    &client_address));
196  const std::string message = "Simple message that will be sent via UDP";
197  pp::NetAddress recvfrom_address;
198  ASSERT_SUBTEST_SUCCESS(PassMessage(&server_socket, &client_socket,
199                                     server_address, message,
200                                     &recvfrom_address));
201  ASSERT_TRUE(EqualNetAddress(recvfrom_address, client_address));
202
203  server_socket.Close();
204  client_socket.Close();
205
206  if (server_socket.GetBoundAddress().pp_resource() != 0)
207    return "PPB_UDPSocket::GetBoundAddress: expected failure";
208
209  PASS();
210}
211
212std::string TestUDPSocket::TestBroadcast() {
213  pp::UDPSocket server1(instance_), server2(instance_);
214
215  ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&server1));
216  ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&server2));
217
218  PP_NetAddress_IPv4 any_ipv4_address = { 0, { 0, 0, 0, 0 } };
219  pp::NetAddress any_address(instance_, any_ipv4_address);
220  ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&server1, any_address));
221  // Fill port field of |server_address|.
222  pp::NetAddress server_address = server1.GetBoundAddress();
223  ASSERT_NE(0, server_address.pp_resource());
224  ASSERT_SUBTEST_SUCCESS(BindUDPSocket(&server2, server_address));
225
226  PP_NetAddress_IPv4 server_ipv4_address;
227  ASSERT_TRUE(server_address.DescribeAsIPv4Address(&server_ipv4_address));
228
229  PP_NetAddress_IPv4 broadcast_ipv4_address = {
230    server_ipv4_address.port, { 0xff, 0xff, 0xff, 0xff }
231  };
232  pp::NetAddress broadcast_address(instance_, broadcast_ipv4_address);
233
234  std::string message;
235  const std::string first_message = "first message";
236  const std::string second_message = "second_message";
237
238  pp::NetAddress recvfrom_address;
239  ASSERT_SUBTEST_SUCCESS(PassMessage(&server1, &server2, broadcast_address,
240                                     first_message, &recvfrom_address));
241  // |first_message| was also received by |server2|.
242  ASSERT_SUBTEST_SUCCESS(ReadSocket(&server2, &recvfrom_address,
243                                    first_message.size(), &message));
244  ASSERT_EQ(first_message, message);
245
246  ASSERT_SUBTEST_SUCCESS(PassMessage(&server2, &server1, broadcast_address,
247                                     second_message, &recvfrom_address));
248  // |second_message| was also received by |server1|.
249  ASSERT_SUBTEST_SUCCESS(ReadSocket(&server1, &recvfrom_address,
250                                    second_message.size(), &message));
251  ASSERT_EQ(second_message, message);
252
253  server1.Close();
254  server2.Close();
255  PASS();
256}
257
258std::string TestUDPSocket::TestSetOption() {
259  pp::UDPSocket socket(instance_);
260
261  ASSERT_SUBTEST_SUCCESS(SetBroadcastOptions(&socket));
262
263  // Try to pass incorrect option value's type.
264  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
265  callback.WaitForResult(socket.SetOption(
266      PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(1), callback.GetCallback()));
267  CHECK_CALLBACK_BEHAVIOR(callback);
268  ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
269
270  callback.WaitForResult(socket.SetOption(
271      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(false), callback.GetCallback()));
272  CHECK_CALLBACK_BEHAVIOR(callback);
273  ASSERT_EQ(PP_OK, callback.result());
274
275  // SEND_BUFFER_SIZE and RECV_BUFFER_SIZE shouldn't be set before the socket is
276  // bound.
277  callback.WaitForResult(socket.SetOption(
278      PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE, pp::Var(4096),
279      callback.GetCallback()));
280  CHECK_CALLBACK_BEHAVIOR(callback);
281  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
282
283  callback.WaitForResult(socket.SetOption(
284      PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE, pp::Var(512),
285      callback.GetCallback()));
286  CHECK_CALLBACK_BEHAVIOR(callback);
287  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
288
289  pp::NetAddress address;
290  ASSERT_SUBTEST_SUCCESS(LookupPortAndBindUDPSocket(&socket, &address));
291
292  // ADDRESS_REUSE and BROADCAST won't take effect after the socket is bound.
293  callback.WaitForResult(socket.SetOption(
294      PP_UDPSOCKET_OPTION_ADDRESS_REUSE, pp::Var(true),
295      callback.GetCallback()));
296  CHECK_CALLBACK_BEHAVIOR(callback);
297  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
298
299  callback.WaitForResult(socket.SetOption(
300      PP_UDPSOCKET_OPTION_BROADCAST, pp::Var(true), callback.GetCallback()));
301  CHECK_CALLBACK_BEHAVIOR(callback);
302  ASSERT_EQ(PP_ERROR_FAILED, callback.result());
303
304  // SEND_BUFFER_SIZE and RECV_BUFFER_SIZE can be set after the socket is bound.
305  callback.WaitForResult(socket.SetOption(
306      PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE, pp::Var(2048),
307      callback.GetCallback()));
308  CHECK_CALLBACK_BEHAVIOR(callback);
309  ASSERT_EQ(PP_OK, callback.result());
310
311  callback.WaitForResult(socket.SetOption(
312      PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE, pp::Var(1024),
313      callback.GetCallback()));
314  CHECK_CALLBACK_BEHAVIOR(callback);
315  ASSERT_EQ(PP_OK, callback.result());
316
317  PASS();
318}
319