Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/test/TestUDPSocket.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "TestCommon.h"
6
#include "gtest/gtest.h"
7
#include "nsIUDPSocket.h"
8
#include "nsISocketTransportService.h"
9
#include "nsISocketTransport.h"
10
#include "nsIOutputStream.h"
11
#include "nsIInputStream.h"
12
#include "nsINetAddr.h"
13
#include "nsIScriptSecurityManager.h"
14
#include "nsITimer.h"
15
#include "nsContentUtils.h"
16
#include "mozilla/net/DNS.h"
17
#include "prerror.h"
18
19
0
#define REQUEST  0x68656c6f
20
0
#define RESPONSE 0x6f6c6568
21
0
#define MULTICAST_TIMEOUT 2000
22
23
enum TestPhase {
24
  TEST_OUTPUT_STREAM,
25
  TEST_SEND_API,
26
  TEST_MULTICAST,
27
  TEST_NONE
28
};
29
30
static TestPhase phase = TEST_NONE;
31
32
static bool CheckMessageContent(nsIUDPMessage *aMessage, uint32_t aExpectedContent)
33
0
{
34
0
  nsCString data;
35
0
  aMessage->GetData(data);
36
0
37
0
  const char* buffer = data.get();
38
0
  uint32_t len = data.Length();
39
0
40
0
  FallibleTArray<uint8_t>& rawData = aMessage->GetDataAsTArray();
41
0
  uint32_t rawLen = rawData.Length();
42
0
43
0
  if (len != rawLen) {
44
0
    ADD_FAILURE() << "Raw data length " << rawLen << " does not match String data length " << len;
45
0
    return false;
46
0
  }
47
0
48
0
  for (uint32_t i = 0; i < len; i++) {
49
0
    if (buffer[i] != rawData[i]) {
50
0
      ADD_FAILURE();
51
0
      return false;
52
0
    }
53
0
  }
54
0
55
0
  uint32_t input = 0;
56
0
  for (uint32_t i = 0; i < len; i++) {
57
0
    input += buffer[i] << (8 * i);
58
0
  }
59
0
60
0
  if (len != sizeof(uint32_t)) {
61
0
    ADD_FAILURE() << "Message length mismatch, expected " << sizeof(uint32_t) <<
62
0
      " got " << len;
63
0
    return false;
64
0
  }
65
0
  if (input != aExpectedContent) {
66
0
    ADD_FAILURE() << "Message content mismatch, expected 0x" <<
67
0
      std::hex << aExpectedContent << " got 0x" << input;
68
0
    return false;
69
0
  }
70
0
71
0
  return true;
72
0
}
73
74
/*
75
 * UDPClientListener: listens for incomming UDP packets
76
 */
77
class UDPClientListener : public nsIUDPSocketListener
78
{
79
protected:
80
  virtual ~UDPClientListener();
81
82
public:
83
  explicit UDPClientListener(WaitForCondition* waiter)
84
    : mWaiter(waiter)
85
0
  { }
86
87
  NS_DECL_THREADSAFE_ISUPPORTS
88
  NS_DECL_NSIUDPSOCKETLISTENER
89
  nsresult mResult = NS_ERROR_FAILURE;
90
  RefPtr<WaitForCondition> mWaiter;
91
};
92
93
NS_IMPL_ISUPPORTS(UDPClientListener, nsIUDPSocketListener)
94
95
0
UDPClientListener::~UDPClientListener() = default;
96
97
NS_IMETHODIMP
98
UDPClientListener::OnPacketReceived(nsIUDPSocket* socket, nsIUDPMessage* message)
99
0
{
100
0
  mResult = NS_OK;
101
0
102
0
  uint16_t port;
103
0
  nsCString ip;
104
0
  nsCOMPtr<nsINetAddr> fromAddr;
105
0
  message->GetFromAddr(getter_AddRefs(fromAddr));
106
0
  fromAddr->GetPort(&port);
107
0
  fromAddr->GetAddress(ip);
108
0
109
0
  if (TEST_SEND_API == phase && CheckMessageContent(message, REQUEST)) {
110
0
    uint32_t count;
111
0
    const uint32_t data = RESPONSE;
112
0
    mResult = socket->SendWithAddr(fromAddr, (const uint8_t*)&data,
113
0
                                   sizeof(uint32_t), &count);
114
0
    if (mResult == NS_OK && count == sizeof(uint32_t)) {
115
0
      SUCCEED();
116
0
    } else {
117
0
      ADD_FAILURE();
118
0
    }
119
0
    return NS_OK;
120
0
  } else if (TEST_OUTPUT_STREAM != phase || !CheckMessageContent(message, RESPONSE)) {
121
0
    mResult = NS_ERROR_FAILURE;
122
0
  }
123
0
124
0
  // Notify thread
125
0
  mWaiter->Notify();
126
0
  return NS_OK;
127
0
}
128
129
NS_IMETHODIMP
130
UDPClientListener::OnStopListening(nsIUDPSocket*, nsresult)
131
0
{
132
0
  mWaiter->Notify();
133
0
  return NS_OK;
134
0
}
135
136
/*
137
 * UDPServerListener: listens for incomming UDP packets
138
 */
139
class UDPServerListener : public nsIUDPSocketListener
140
{
141
protected:
142
  virtual ~UDPServerListener();
143
144
public:
145
  explicit UDPServerListener(WaitForCondition* waiter)
146
    : mWaiter(waiter)
147
0
  { }
148
149
  NS_DECL_THREADSAFE_ISUPPORTS
150
  NS_DECL_NSIUDPSOCKETLISTENER
151
152
  nsresult mResult = NS_ERROR_FAILURE;
153
  RefPtr<WaitForCondition> mWaiter;
154
};
155
156
NS_IMPL_ISUPPORTS(UDPServerListener, nsIUDPSocketListener)
157
158
0
UDPServerListener::~UDPServerListener() = default;
159
160
NS_IMETHODIMP
161
UDPServerListener::OnPacketReceived(nsIUDPSocket* socket, nsIUDPMessage* message)
162
0
{
163
0
  mResult = NS_OK;
164
0
165
0
  uint16_t port;
166
0
  nsCString ip;
167
0
  nsCOMPtr<nsINetAddr> fromAddr;
168
0
  message->GetFromAddr(getter_AddRefs(fromAddr));
169
0
  fromAddr->GetPort(&port);
170
0
  fromAddr->GetAddress(ip);
171
0
  SUCCEED();
172
0
173
0
  if (TEST_OUTPUT_STREAM == phase && CheckMessageContent(message, REQUEST))
174
0
  {
175
0
    nsCOMPtr<nsIOutputStream> outstream;
176
0
    message->GetOutputStream(getter_AddRefs(outstream));
177
0
178
0
    uint32_t count;
179
0
    const uint32_t data = RESPONSE;
180
0
    mResult = outstream->Write((const char*)&data, sizeof(uint32_t), &count);
181
0
182
0
    if (mResult == NS_OK && count == sizeof(uint32_t)) {
183
0
      SUCCEED();
184
0
    } else {
185
0
      ADD_FAILURE();
186
0
    }
187
0
    return NS_OK;
188
0
  } else if (TEST_MULTICAST == phase && CheckMessageContent(message, REQUEST)) {
189
0
    mResult = NS_OK;
190
0
  } else if (TEST_SEND_API != phase || !CheckMessageContent(message, RESPONSE)) {
191
0
    mResult = NS_ERROR_FAILURE;
192
0
  }
193
0
194
0
  // Notify thread
195
0
  mWaiter->Notify();
196
0
  return NS_OK;
197
0
}
198
199
NS_IMETHODIMP
200
UDPServerListener::OnStopListening(nsIUDPSocket*, nsresult)
201
0
{
202
0
  mWaiter->Notify();
203
0
  return NS_OK;
204
0
}
205
206
/**
207
 * Multicast timer callback: detects delivery failure
208
 */
209
class MulticastTimerCallback : public nsITimerCallback
210
{
211
protected:
212
  virtual ~MulticastTimerCallback();
213
214
public:
215
  explicit MulticastTimerCallback(WaitForCondition* waiter)
216
    : mResult(NS_ERROR_NOT_INITIALIZED)
217
    , mWaiter(waiter)
218
0
  { }
219
220
  NS_DECL_THREADSAFE_ISUPPORTS
221
  NS_DECL_NSITIMERCALLBACK
222
223
  nsresult mResult;
224
  RefPtr<WaitForCondition> mWaiter;
225
};
226
227
NS_IMPL_ISUPPORTS(MulticastTimerCallback, nsITimerCallback)
228
229
0
MulticastTimerCallback::~MulticastTimerCallback() = default;
230
231
NS_IMETHODIMP
232
MulticastTimerCallback::Notify(nsITimer* timer)
233
0
{
234
0
  if (TEST_MULTICAST != phase) {
235
0
    return NS_OK;
236
0
  }
237
0
  // Multicast ping failed
238
0
  printf("Multicast ping timeout expired\n");
239
0
  mResult = NS_ERROR_FAILURE;
240
0
  mWaiter->Notify();
241
0
  return NS_OK;
242
0
}
243
244
/**** Main ****/
245
246
TEST(TestUDPSocket, TestUDPSocketMain)
247
0
{
248
0
  nsresult rv;
249
0
250
0
  // Create UDPSocket
251
0
  nsCOMPtr<nsIUDPSocket> server, client;
252
0
  server = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
253
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
254
0
255
0
  client = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
256
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
257
0
258
0
  RefPtr<WaitForCondition> waiter = new WaitForCondition();
259
0
260
0
  // Create UDPServerListener to process UDP packets
261
0
  RefPtr<UDPServerListener> serverListener = new UDPServerListener(waiter);
262
0
263
0
  nsCOMPtr<nsIPrincipal> systemPrincipal = nsContentUtils::GetSystemPrincipal();
264
0
265
0
  // Bind server socket to 0.0.0.0
266
0
  rv = server->Init(0, false, systemPrincipal, true, 0);
267
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
268
0
  int32_t serverPort;
269
0
  server->GetPort(&serverPort);
270
0
  server->AsyncListen(serverListener);
271
0
272
0
  // Bind clinet on arbitrary port
273
0
  RefPtr<UDPClientListener> clientListener = new UDPClientListener(waiter);
274
0
  client->Init(0, false, systemPrincipal, true, 0);
275
0
  client->AsyncListen(clientListener);
276
0
277
0
  // Write data to server
278
0
  uint32_t count;
279
0
  const uint32_t data = REQUEST;
280
0
281
0
  phase = TEST_OUTPUT_STREAM;
282
0
  rv = client->Send(NS_LITERAL_CSTRING("127.0.0.1"), serverPort, (uint8_t*)&data, sizeof(uint32_t), &count);
283
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
284
0
  EXPECT_EQ(count, sizeof(uint32_t));
285
0
286
0
  // Wait for server
287
0
  waiter->Wait(1);
288
0
  ASSERT_TRUE(NS_SUCCEEDED(serverListener->mResult));
289
0
290
0
  // Read response from server
291
0
  ASSERT_TRUE(NS_SUCCEEDED(clientListener->mResult));
292
0
293
0
  mozilla::net::NetAddr clientAddr;
294
0
  rv = client->GetAddress(&clientAddr);
295
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
296
0
  // The client address is 0.0.0.0, but Windows won't receive packets there, so
297
0
  // use 127.0.0.1 explicitly
298
0
  clientAddr.inet.ip = PR_htonl(127 << 24 | 1);
299
0
300
0
  phase = TEST_SEND_API;
301
0
  rv = server->SendWithAddress(&clientAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
302
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
303
0
  EXPECT_EQ(count, sizeof(uint32_t));
304
0
305
0
  // Wait for server
306
0
  waiter->Wait(1);
307
0
  ASSERT_TRUE(NS_SUCCEEDED(serverListener->mResult));
308
0
309
0
  // Read response from server
310
0
  ASSERT_TRUE(NS_SUCCEEDED(clientListener->mResult));
311
0
312
0
  // Setup timer to detect multicast failure
313
0
  nsCOMPtr<nsITimer> timer = NS_NewTimer();
314
0
  ASSERT_TRUE(timer);
315
0
  RefPtr<MulticastTimerCallback> timerCb = new MulticastTimerCallback(waiter);
316
0
317
0
  // Join multicast group
318
0
  printf("Joining multicast group\n");
319
0
  phase = TEST_MULTICAST;
320
0
  mozilla::net::NetAddr multicastAddr;
321
0
  multicastAddr.inet.family = AF_INET;
322
0
  multicastAddr.inet.ip = PR_htonl(224 << 24 | 255);
323
0
  multicastAddr.inet.port = PR_htons(serverPort);
324
0
  rv = server->JoinMulticastAddr(multicastAddr, nullptr);
325
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
326
0
327
0
  // Send multicast ping
328
0
  timerCb->mResult = NS_OK;
329
0
  timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
330
0
  rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
331
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
332
0
  EXPECT_EQ(count, sizeof(uint32_t));
333
0
334
0
  // Wait for server to receive successfully
335
0
  waiter->Wait(1);
336
0
  ASSERT_TRUE(NS_SUCCEEDED(serverListener->mResult));
337
0
  ASSERT_TRUE(NS_SUCCEEDED(timerCb->mResult));
338
0
  timer->Cancel();
339
0
340
0
  // Disable multicast loopback
341
0
  printf("Disable multicast loopback\n");
342
0
  client->SetMulticastLoopback(false);
343
0
  server->SetMulticastLoopback(false);
344
0
345
0
  // Send multicast ping
346
0
  timerCb->mResult = NS_OK;
347
0
  timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
348
0
  rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
349
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
350
0
  EXPECT_EQ(count, sizeof(uint32_t));
351
0
352
0
  // Wait for server to fail to receive
353
0
  waiter->Wait(1);
354
0
  ASSERT_FALSE(NS_SUCCEEDED(timerCb->mResult));
355
0
  timer->Cancel();
356
0
357
0
  // Reset state
358
0
  client->SetMulticastLoopback(true);
359
0
  server->SetMulticastLoopback(true);
360
0
361
0
  // Change multicast interface
362
0
  mozilla::net::NetAddr loopbackAddr;
363
0
  loopbackAddr.inet.family = AF_INET;
364
0
  loopbackAddr.inet.ip = PR_htonl(INADDR_LOOPBACK);
365
0
  client->SetMulticastInterfaceAddr(loopbackAddr);
366
0
367
0
  // Send multicast ping
368
0
  timerCb->mResult = NS_OK;
369
0
  timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
370
0
  rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
371
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
372
0
  EXPECT_EQ(count, sizeof(uint32_t));
373
0
374
0
  // Wait for server to fail to receive
375
0
  waiter->Wait(1);
376
0
  ASSERT_FALSE(NS_SUCCEEDED(timerCb->mResult));
377
0
  timer->Cancel();
378
0
379
0
  // Reset state
380
0
  mozilla::net::NetAddr anyAddr;
381
0
  anyAddr.inet.family = AF_INET;
382
0
  anyAddr.inet.ip = PR_htonl(INADDR_ANY);
383
0
  client->SetMulticastInterfaceAddr(anyAddr);
384
0
385
0
  // Leave multicast group
386
0
  rv = server->LeaveMulticastAddr(multicastAddr, nullptr);
387
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
388
0
389
0
  // Send multicast ping
390
0
  timerCb->mResult = NS_OK;
391
0
  timer->InitWithCallback(timerCb, MULTICAST_TIMEOUT, nsITimer::TYPE_ONE_SHOT);
392
0
  rv = client->SendWithAddress(&multicastAddr, (uint8_t*)&data, sizeof(uint32_t), &count);
393
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
394
0
  EXPECT_EQ(count, sizeof(uint32_t));
395
0
396
0
  // Wait for server to fail to receive
397
0
  waiter->Wait(1);
398
0
  ASSERT_FALSE(NS_SUCCEEDED(timerCb->mResult));
399
0
  timer->Cancel();
400
0
401
0
  goto close; // suppress warning about unused label
402
0
403
0
close:
404
0
  // Close server
405
0
  server->Close();
406
0
  client->Close();
407
0
408
0
  // Wait for client and server to see closing
409
0
  waiter->Wait(2);
410
0
}