|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 // Original author: ekr@rtfm.com |
|
8 |
|
9 #include <iostream> |
|
10 |
|
11 #include "nspr.h" |
|
12 #include "nss.h" |
|
13 #include "ssl.h" |
|
14 |
|
15 #include "mozilla/Scoped.h" |
|
16 |
|
17 extern "C" { |
|
18 #include "nr_api.h" |
|
19 #include "nr_socket.h" |
|
20 #include "nr_socket_buffered_stun.h" |
|
21 #include "transport_addr.h" |
|
22 #include "stun.h" |
|
23 } |
|
24 |
|
25 #include "databuffer.h" |
|
26 #include "mtransport_test_utils.h" |
|
27 |
|
28 #include "nr_socket_prsock.h" |
|
29 |
|
30 #define GTEST_HAS_RTTI 0 |
|
31 #include "gtest/gtest.h" |
|
32 #include "gtest_utils.h" |
|
33 |
|
34 using namespace mozilla; |
|
35 MtransportTestUtils *test_utils; |
|
36 |
|
37 static uint8_t kStunMessage[] = { |
|
38 0x00, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42, |
|
39 0x9b, 0x90, 0xbe, 0x2c, 0xae, 0x1a, 0x0c, 0xa8, |
|
40 0xa0, 0xd6, 0x8b, 0x08, 0x80, 0x28, 0x00, 0x04, |
|
41 0xdb, 0x35, 0x5f, 0xaa |
|
42 }; |
|
43 static size_t kStunMessageLen = sizeof(kStunMessage); |
|
44 |
|
45 class DummySocket : public NrSocketBase { |
|
46 public: |
|
47 DummySocket() |
|
48 : writable_(UINT_MAX), |
|
49 write_buffer_(nullptr), |
|
50 readable_(UINT_MAX), |
|
51 read_buffer_(nullptr), |
|
52 cb_(nullptr), |
|
53 cb_arg_(nullptr), |
|
54 self_(nullptr) {} |
|
55 |
|
56 // the nr_socket APIs |
|
57 virtual int create(nr_transport_addr *addr) { |
|
58 return 0; |
|
59 } |
|
60 |
|
61 virtual int sendto(const void *msg, size_t len, |
|
62 int flags, nr_transport_addr *to) { |
|
63 MOZ_CRASH(); |
|
64 return 0; |
|
65 } |
|
66 |
|
67 virtual int recvfrom(void * buf, size_t maxlen, |
|
68 size_t *len, int flags, |
|
69 nr_transport_addr *from) { |
|
70 MOZ_CRASH(); |
|
71 return 0; |
|
72 } |
|
73 |
|
74 virtual int getaddr(nr_transport_addr *addrp) { |
|
75 MOZ_CRASH(); |
|
76 return 0; |
|
77 } |
|
78 |
|
79 virtual void close() { |
|
80 } |
|
81 |
|
82 virtual int connect(nr_transport_addr *addr) { |
|
83 return 0; |
|
84 } |
|
85 |
|
86 virtual int write(const void *msg, size_t len, size_t *written) { |
|
87 // Shouldn't be anything here. |
|
88 EXPECT_EQ(nullptr, write_buffer_.get()); |
|
89 |
|
90 size_t to_write = std::min(len, writable_); |
|
91 |
|
92 if (to_write) { |
|
93 write_buffer_ = new DataBuffer( |
|
94 static_cast<const uint8_t *>(msg), to_write); |
|
95 *written = to_write; |
|
96 } |
|
97 |
|
98 return 0; |
|
99 } |
|
100 |
|
101 virtual int read(void* buf, size_t maxlen, size_t *len) { |
|
102 if (!read_buffer_.get()) { |
|
103 return R_WOULDBLOCK; |
|
104 } |
|
105 |
|
106 size_t to_read = std::min(read_buffer_->len(), |
|
107 std::min(maxlen, readable_)); |
|
108 |
|
109 memcpy(buf, read_buffer_->data(), to_read); |
|
110 *len = to_read; |
|
111 |
|
112 if (to_read < read_buffer_->len()) { |
|
113 read_buffer_ = new DataBuffer(read_buffer_->data() + to_read, |
|
114 read_buffer_->len() - to_read); |
|
115 } else { |
|
116 read_buffer_ = nullptr; |
|
117 } |
|
118 |
|
119 return 0; |
|
120 } |
|
121 |
|
122 // Implementations of the async_event APIs. |
|
123 // These are no-ops because we handle scheduling manually |
|
124 // for test purposes. |
|
125 virtual int async_wait(int how, NR_async_cb cb, void *cb_arg, |
|
126 char *function, int line) { |
|
127 EXPECT_EQ(nullptr, cb_); |
|
128 cb_ = cb; |
|
129 cb_arg_ = cb_arg; |
|
130 |
|
131 return 0; |
|
132 } |
|
133 |
|
134 virtual int cancel(int how) { |
|
135 cb_ = nullptr; |
|
136 cb_arg_ = nullptr; |
|
137 |
|
138 return 0; |
|
139 } |
|
140 |
|
141 |
|
142 // Read/Manipulate the current state. |
|
143 void CheckWriteBuffer(uint8_t *data, size_t len) { |
|
144 if (!len) { |
|
145 EXPECT_EQ(nullptr, write_buffer_.get()); |
|
146 } else { |
|
147 EXPECT_NE(nullptr, write_buffer_.get()); |
|
148 ASSERT_EQ(len, write_buffer_->len()); |
|
149 ASSERT_EQ(0, memcmp(data, write_buffer_->data(), len)); |
|
150 } |
|
151 } |
|
152 |
|
153 void ClearWriteBuffer() { |
|
154 write_buffer_ = nullptr; |
|
155 } |
|
156 |
|
157 void SetWritable(size_t val) { |
|
158 writable_ = val; |
|
159 } |
|
160 |
|
161 void FireWritableCb() { |
|
162 NR_async_cb cb = cb_; |
|
163 void *cb_arg = cb_arg_; |
|
164 |
|
165 cb_ = nullptr; |
|
166 cb_arg_ = nullptr; |
|
167 |
|
168 cb(this, NR_ASYNC_WAIT_WRITE, cb_arg); |
|
169 } |
|
170 |
|
171 void SetReadBuffer(uint8_t *data, size_t len) { |
|
172 EXPECT_EQ(nullptr, write_buffer_.get()); |
|
173 read_buffer_ = new DataBuffer(data, len); |
|
174 } |
|
175 |
|
176 void ClearReadBuffer() { |
|
177 read_buffer_ = nullptr; |
|
178 } |
|
179 |
|
180 void SetReadable(size_t val) { |
|
181 readable_ = val; |
|
182 } |
|
183 |
|
184 nr_socket *get_nr_socket() { |
|
185 if (!self_) { |
|
186 int r = nr_socket_create_int(this, vtbl(), &self_); |
|
187 AddRef(); |
|
188 if (r) |
|
189 return nullptr; |
|
190 } |
|
191 |
|
192 return self_; |
|
193 } |
|
194 |
|
195 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket); |
|
196 |
|
197 private: |
|
198 DISALLOW_COPY_ASSIGN(DummySocket); |
|
199 |
|
200 size_t writable_; // Amount we allow someone to write. |
|
201 ScopedDeletePtr<DataBuffer> write_buffer_; |
|
202 size_t readable_; // Amount we allow someone to read. |
|
203 ScopedDeletePtr<DataBuffer> read_buffer_; |
|
204 |
|
205 NR_async_cb cb_; |
|
206 void *cb_arg_; |
|
207 nr_socket *self_; |
|
208 }; |
|
209 |
|
210 class BufferedStunSocketTest : public ::testing::Test { |
|
211 public: |
|
212 BufferedStunSocketTest() |
|
213 : dummy_(nullptr), |
|
214 test_socket_(nullptr) {} |
|
215 |
|
216 ~BufferedStunSocketTest() { |
|
217 nr_socket_destroy(&test_socket_); |
|
218 } |
|
219 |
|
220 void SetUp() { |
|
221 ScopedDeletePtr<DummySocket> dummy(new DummySocket()); |
|
222 |
|
223 int r = nr_socket_buffered_stun_create( |
|
224 dummy->get_nr_socket(), |
|
225 kStunMessageLen, |
|
226 &test_socket_); |
|
227 ASSERT_EQ(0, r); |
|
228 dummy_ = dummy.forget(); // Now owned by test_socket_. |
|
229 |
|
230 r = nr_ip4_str_port_to_transport_addr( |
|
231 (char *)"192.0.2.133", 3333, IPPROTO_TCP, &remote_addr_); |
|
232 ASSERT_EQ(0, r); |
|
233 |
|
234 r = nr_socket_connect(test_socket_, |
|
235 &remote_addr_); |
|
236 ASSERT_EQ(0, r); |
|
237 } |
|
238 |
|
239 nr_socket *socket() { return test_socket_; } |
|
240 |
|
241 protected: |
|
242 DummySocket *dummy_; |
|
243 nr_socket *test_socket_; |
|
244 nr_transport_addr remote_addr_; |
|
245 }; |
|
246 |
|
247 |
|
248 TEST_F(BufferedStunSocketTest, TestCreate) { |
|
249 } |
|
250 |
|
251 TEST_F(BufferedStunSocketTest, TestSendTo) { |
|
252 int r = nr_socket_sendto(test_socket_, |
|
253 kStunMessage, |
|
254 kStunMessageLen, |
|
255 0, &remote_addr_); |
|
256 ASSERT_EQ(0, r); |
|
257 |
|
258 dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen); |
|
259 } |
|
260 |
|
261 TEST_F(BufferedStunSocketTest, TestSendToBuffered) { |
|
262 dummy_->SetWritable(0); |
|
263 |
|
264 int r = nr_socket_sendto(test_socket_, |
|
265 kStunMessage, |
|
266 kStunMessageLen, |
|
267 0, &remote_addr_); |
|
268 ASSERT_EQ(0, r); |
|
269 |
|
270 dummy_->CheckWriteBuffer(nullptr, 0); |
|
271 |
|
272 dummy_->SetWritable(kStunMessageLen); |
|
273 dummy_->FireWritableCb(); |
|
274 dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen); |
|
275 } |
|
276 |
|
277 TEST_F(BufferedStunSocketTest, TestSendFullThenDrain) { |
|
278 dummy_->SetWritable(0); |
|
279 |
|
280 for (;;) { |
|
281 int r = nr_socket_sendto(test_socket_, |
|
282 kStunMessage, |
|
283 kStunMessageLen, |
|
284 0, &remote_addr_); |
|
285 if (r == R_WOULDBLOCK) |
|
286 break; |
|
287 |
|
288 ASSERT_EQ(0, r); |
|
289 } |
|
290 |
|
291 // Nothing was written. |
|
292 dummy_->CheckWriteBuffer(nullptr, 0); |
|
293 |
|
294 // Now flush. |
|
295 dummy_->SetWritable(kStunMessageLen); |
|
296 dummy_->FireWritableCb(); |
|
297 dummy_->ClearWriteBuffer(); |
|
298 |
|
299 // Verify we can write something. |
|
300 int r = nr_socket_sendto(test_socket_, |
|
301 kStunMessage, |
|
302 kStunMessageLen, |
|
303 0, &remote_addr_); |
|
304 ASSERT_EQ(0, r); |
|
305 |
|
306 // And that it appears. |
|
307 dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen); |
|
308 } |
|
309 |
|
310 TEST_F(BufferedStunSocketTest, TestSendToPartialBuffered) { |
|
311 dummy_->SetWritable(10); |
|
312 |
|
313 int r = nr_socket_sendto(test_socket_, |
|
314 kStunMessage, |
|
315 kStunMessageLen, |
|
316 0, &remote_addr_); |
|
317 ASSERT_EQ(0, r); |
|
318 |
|
319 dummy_->CheckWriteBuffer(kStunMessage, 10); |
|
320 dummy_->ClearWriteBuffer(); |
|
321 |
|
322 dummy_->SetWritable(kStunMessageLen); |
|
323 dummy_->FireWritableCb(); |
|
324 dummy_->CheckWriteBuffer(kStunMessage + 10, kStunMessageLen - 10); |
|
325 } |
|
326 |
|
327 TEST_F(BufferedStunSocketTest, TestSendToReject) { |
|
328 dummy_->SetWritable(0); |
|
329 |
|
330 int r = nr_socket_sendto(test_socket_, |
|
331 kStunMessage, |
|
332 kStunMessageLen, |
|
333 0, &remote_addr_); |
|
334 ASSERT_EQ(0, r); |
|
335 |
|
336 dummy_->CheckWriteBuffer(nullptr, 0); |
|
337 |
|
338 r = nr_socket_sendto(test_socket_, |
|
339 kStunMessage, |
|
340 kStunMessageLen, |
|
341 0, &remote_addr_); |
|
342 ASSERT_EQ(R_WOULDBLOCK, r); |
|
343 |
|
344 dummy_->CheckWriteBuffer(nullptr, 0); |
|
345 } |
|
346 |
|
347 TEST_F(BufferedStunSocketTest, TestSendToWrongAddr) { |
|
348 nr_transport_addr addr; |
|
349 |
|
350 int r = nr_ip4_str_port_to_transport_addr( |
|
351 (char *)"192.0.2.134", 3333, IPPROTO_TCP, &addr); |
|
352 ASSERT_EQ(0, r); |
|
353 |
|
354 r = nr_socket_sendto(test_socket_, |
|
355 kStunMessage, |
|
356 kStunMessageLen, |
|
357 0, &addr); |
|
358 ASSERT_EQ(R_BAD_DATA, r); |
|
359 } |
|
360 |
|
361 TEST_F(BufferedStunSocketTest, TestReceiveRecvFrom) { |
|
362 dummy_->SetReadBuffer(kStunMessage, kStunMessageLen); |
|
363 |
|
364 unsigned char tmp[2048]; |
|
365 size_t len; |
|
366 nr_transport_addr addr; |
|
367 |
|
368 int r = nr_socket_recvfrom(test_socket_, |
|
369 tmp, sizeof(tmp), &len, 0, |
|
370 &addr); |
|
371 ASSERT_EQ(0, r); |
|
372 ASSERT_EQ(kStunMessageLen, len); |
|
373 ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen)); |
|
374 ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_, |
|
375 NR_TRANSPORT_ADDR_CMP_MODE_ALL)); |
|
376 } |
|
377 |
|
378 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromPartial) { |
|
379 dummy_->SetReadBuffer(kStunMessage, 15); |
|
380 |
|
381 unsigned char tmp[2048]; |
|
382 size_t len; |
|
383 nr_transport_addr addr; |
|
384 |
|
385 int r = nr_socket_recvfrom(test_socket_, |
|
386 tmp, sizeof(tmp), &len, 0, |
|
387 &addr); |
|
388 ASSERT_EQ(R_WOULDBLOCK, r); |
|
389 |
|
390 |
|
391 dummy_->SetReadBuffer(kStunMessage + 15, kStunMessageLen - 15); |
|
392 |
|
393 r = nr_socket_recvfrom(test_socket_, |
|
394 tmp, sizeof(tmp), &len, 0, |
|
395 &addr); |
|
396 ASSERT_EQ(0, r); |
|
397 ASSERT_EQ(kStunMessageLen, len); |
|
398 ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen)); |
|
399 ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_, |
|
400 NR_TRANSPORT_ADDR_CMP_MODE_ALL)); |
|
401 |
|
402 r = nr_socket_recvfrom(test_socket_, |
|
403 tmp, sizeof(tmp), &len, 0, |
|
404 &addr); |
|
405 ASSERT_EQ(R_WOULDBLOCK, r); |
|
406 } |
|
407 |
|
408 |
|
409 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromGarbage) { |
|
410 uint8_t garbage[50]; |
|
411 memset(garbage, 0xff, sizeof(garbage)); |
|
412 |
|
413 dummy_->SetReadBuffer(garbage, sizeof(garbage)); |
|
414 |
|
415 unsigned char tmp[2048]; |
|
416 size_t len; |
|
417 nr_transport_addr addr; |
|
418 int r = nr_socket_recvfrom(test_socket_, |
|
419 tmp, sizeof(tmp), &len, 0, |
|
420 &addr); |
|
421 ASSERT_EQ(R_BAD_DATA, r); |
|
422 |
|
423 r = nr_socket_recvfrom(test_socket_, |
|
424 tmp, sizeof(tmp), &len, 0, |
|
425 &addr); |
|
426 ASSERT_EQ(R_FAILED, r); |
|
427 } |
|
428 |
|
429 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromTooShort) { |
|
430 dummy_->SetReadBuffer(kStunMessage, kStunMessageLen); |
|
431 |
|
432 unsigned char tmp[2048]; |
|
433 size_t len; |
|
434 nr_transport_addr addr; |
|
435 |
|
436 int r = nr_socket_recvfrom(test_socket_, |
|
437 tmp, kStunMessageLen - 1, &len, 0, |
|
438 &addr); |
|
439 ASSERT_EQ(R_BAD_ARGS, r); |
|
440 } |
|
441 |
|
442 TEST_F(BufferedStunSocketTest, TestReceiveRecvFromReallyLong) { |
|
443 uint8_t garbage[4096]; |
|
444 memset(garbage, 0xff, sizeof(garbage)); |
|
445 memcpy(garbage, kStunMessage, kStunMessageLen); |
|
446 nr_stun_message_header *hdr = reinterpret_cast<nr_stun_message_header *> |
|
447 (garbage); |
|
448 hdr->length = htons(3000); |
|
449 |
|
450 dummy_->SetReadBuffer(garbage, sizeof(garbage)); |
|
451 |
|
452 unsigned char tmp[4096]; |
|
453 size_t len; |
|
454 nr_transport_addr addr; |
|
455 |
|
456 int r = nr_socket_recvfrom(test_socket_, |
|
457 tmp, kStunMessageLen - 1, &len, 0, |
|
458 &addr); |
|
459 ASSERT_EQ(R_BAD_DATA, r); |
|
460 } |
|
461 |
|
462 |
|
463 |
|
464 int main(int argc, char **argv) |
|
465 { |
|
466 test_utils = new MtransportTestUtils(); |
|
467 NSS_NoDB_Init(nullptr); |
|
468 NSS_SetDomesticPolicy(); |
|
469 |
|
470 // Start the tests |
|
471 ::testing::InitGoogleTest(&argc, argv); |
|
472 |
|
473 int rv = RUN_ALL_TESTS(); |
|
474 |
|
475 delete test_utils; |
|
476 return rv; |
|
477 } |