stunserver.cpp (19692B)
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 /* 10 Original code from nICEr and nrappkit. 11 12 nICEr copyright: 13 14 Copyright (c) 2007, Adobe Systems, Incorporated 15 All rights reserved. 16 17 Redistribution and use in source and binary forms, with or without 18 modification, are permitted provided that the following conditions are 19 met: 20 21 * Redistributions of source code must retain the above copyright 22 notice, this list of conditions and the following disclaimer. 23 24 * Redistributions in binary form must reproduce the above copyright 25 notice, this list of conditions and the following disclaimer in the 26 documentation and/or other materials provided with the distribution. 27 28 * Neither the name of Adobe Systems, Network Resonance nor the names of its 29 contributors may be used to endorse or promote products derived from 30 this software without specific prior written permission. 31 32 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 33 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 34 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 35 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 36 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 37 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 38 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 39 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 40 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 41 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 42 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 44 45 nrappkit copyright: 46 47 Copyright (C) 2001-2003, Network Resonance, Inc. 48 Copyright (C) 2006, Network Resonance, Inc. 49 All Rights Reserved 50 51 Redistribution and use in source and binary forms, with or without 52 modification, are permitted provided that the following conditions 53 are met: 54 55 1. Redistributions of source code must retain the above copyright 56 notice, this list of conditions and the following disclaimer. 57 2. Redistributions in binary form must reproduce the above copyright 58 notice, this list of conditions and the following disclaimer in the 59 documentation and/or other materials provided with the distribution. 60 3. Neither the name of Network Resonance, Inc. nor the name of any 61 contributors to this software may be used to endorse or promote 62 products derived from this software without specific prior written 63 permission. 64 65 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 66 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 67 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 68 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 69 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 70 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 71 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 72 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 73 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 74 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 75 POSSIBILITY OF SUCH DAMAGE. 76 77 78 ekr@rtfm.com Thu Dec 20 20:14:49 2001 79 */ 80 #include "logging.h" 81 #include "mediapacket.h" 82 #include "mozilla/UniquePtr.h" 83 84 // mozilla/utils.h defines this as well 85 #ifdef UNIMPLEMENTED 86 # undef UNIMPLEMENTED 87 #endif 88 89 extern "C" { 90 // clang-format off 91 #include "nr_api.h" 92 #include "async_wait.h" 93 #include "async_timer.h" 94 #include "nr_socket.h" 95 #include "nr_socket_local.h" 96 #include "transport_addr.h" 97 #include "stun_util.h" 98 #include "registry.h" 99 #include "nr_socket_buffered_stun.h" 100 #include "addrs.h" 101 // clang-format on 102 } 103 104 #include <string> 105 106 #include "stunserver.h" 107 108 MOZ_MTLOG_MODULE("stunserver"); 109 110 namespace mozilla { 111 112 // Wrapper nr_socket which allows us to lie to the stun server about the 113 // IP address. 114 struct nr_socket_wrapped { 115 nr_socket* sock_; 116 nr_transport_addr addr_; 117 }; 118 119 static int nr_socket_wrapped_destroy(void** objp) { 120 if (!objp || !*objp) return 0; 121 122 nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(*objp); 123 *objp = nullptr; 124 125 delete wrapped; 126 127 return 0; 128 } 129 130 static int nr_socket_wrapped_sendto(void* obj, const void* msg, size_t len, 131 int flags, const nr_transport_addr* addr) { 132 nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); 133 134 return nr_socket_sendto(wrapped->sock_, msg, len, flags, &wrapped->addr_); 135 } 136 137 static int nr_socket_wrapped_recvfrom(void* obj, void* restrict buf, 138 size_t maxlen, size_t* len, int flags, 139 nr_transport_addr* addr) { 140 nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); 141 142 return nr_socket_recvfrom(wrapped->sock_, buf, maxlen, len, flags, addr); 143 } 144 145 static int nr_socket_wrapped_getfd(void* obj, NR_SOCKET* fd) { 146 nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); 147 148 return nr_socket_getfd(wrapped->sock_, fd); 149 } 150 151 static int nr_socket_wrapped_getaddr(void* obj, nr_transport_addr* addrp) { 152 nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(obj); 153 154 return nr_socket_getaddr(wrapped->sock_, addrp); 155 } 156 157 static int nr_socket_wrapped_close(void* obj) { MOZ_CRASH(); } 158 159 static int nr_socket_wrapped_set_send_addr(nr_socket* sock, 160 nr_transport_addr* addr) { 161 nr_socket_wrapped* wrapped = static_cast<nr_socket_wrapped*>(sock->obj); 162 163 return nr_transport_addr_copy(&wrapped->addr_, addr); 164 } 165 166 static nr_socket_vtbl nr_socket_wrapped_vtbl = {2, 167 nr_socket_wrapped_destroy, 168 nr_socket_wrapped_sendto, 169 nr_socket_wrapped_recvfrom, 170 nr_socket_wrapped_getfd, 171 nr_socket_wrapped_getaddr, 172 nullptr, 173 nullptr, 174 nullptr, 175 nr_socket_wrapped_close, 176 nullptr, 177 nullptr}; 178 179 int nr_socket_wrapped_create(nr_socket* inner, nr_socket** outp) { 180 auto wrapped = MakeUnique<nr_socket_wrapped>(); 181 182 wrapped->sock_ = inner; 183 184 int r = nr_socket_create_int(wrapped.get(), &nr_socket_wrapped_vtbl, outp); 185 if (r) return r; 186 187 (void)wrapped.release(); 188 return 0; 189 } 190 191 // Instance static. 192 // Note: Calling Create() at static init time is not going to be safe, since 193 // we have no reason to expect this will be initted to a nullptr yet. 194 TestStunServer* TestStunServer::instance; 195 TestStunTcpServer* TestStunTcpServer::instance; 196 TestStunServer* TestStunServer::instance6; 197 TestStunTcpServer* TestStunTcpServer::instance6; 198 uint16_t TestStunServer::instance_port = 3478; 199 uint16_t TestStunTcpServer::instance_port = 3478; 200 201 TestStunServer::~TestStunServer() { 202 // TODO(ekr@rtfm.com): Put this on the right thread. 203 204 // Unhook callback from our listen socket. 205 if (listen_sock_) { 206 NR_SOCKET fd; 207 if (!nr_socket_getfd(listen_sock_, &fd)) { 208 NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); 209 } 210 } 211 212 // Free up stun context and network resources 213 nr_stun_server_ctx_destroy(&stun_server_); 214 nr_socket_destroy(&listen_sock_); 215 nr_socket_destroy(&send_sock_); 216 217 // Make sure we aren't still waiting on a deferred response timer to pop 218 if (timer_handle_) NR_async_timer_cancel(timer_handle_); 219 220 delete response_addr_; 221 } 222 223 int TestStunServer::SetInternalPort(nr_local_addr* addr, uint16_t port) { 224 if (nr_transport_addr_set_port(&addr->addr, port)) { 225 MOZ_MTLOG(ML_ERROR, "Couldn't set port"); 226 return R_INTERNAL; 227 } 228 229 if (nr_transport_addr_fmt_addr_string(&addr->addr)) { 230 MOZ_MTLOG(ML_ERROR, "Couldn't re-set addr string"); 231 return R_INTERNAL; 232 } 233 234 return 0; 235 } 236 237 int TestStunServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { 238 int r = SetInternalPort(addr, port); 239 240 if (r) return r; 241 242 if (nr_socket_local_create(nullptr, &addr->addr, &listen_sock_)) { 243 MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket"); 244 return R_ALREADY; 245 } 246 247 return 0; 248 } 249 250 static int addressFamilyToIpVersion(int address_family) { 251 switch (address_family) { 252 case AF_INET: 253 return NR_IPV4; 254 case AF_INET6: 255 return NR_IPV6; 256 default: 257 MOZ_CRASH(); 258 } 259 return NR_IPV4; 260 } 261 262 int TestStunServer::Initialize(int address_family) { 263 static const size_t max_addrs = 100; 264 nr_local_addr addrs[max_addrs]; 265 int addr_ct; 266 int r; 267 int i; 268 269 r = nr_stun_get_addrs(addrs, max_addrs, &addr_ct); 270 if (r) { 271 MOZ_MTLOG(ML_ERROR, "Couldn't retrieve addresses"); 272 return R_INTERNAL; 273 } 274 275 // removes duplicate, loopback, and link_local addrs 276 r = nr_stun_filter_addrs(addrs, true, true, &addr_ct); 277 if (r) { 278 MOZ_MTLOG(ML_ERROR, "Couldn't filter addresses"); 279 return R_INTERNAL; 280 } 281 282 if (addr_ct < 1) { 283 MOZ_MTLOG(ML_ERROR, "No local addresses"); 284 return R_INTERNAL; 285 } 286 287 for (i = 0; i < addr_ct; ++i) { 288 if (addrs[i].addr.ip_version == addressFamilyToIpVersion(address_family)) { 289 break; 290 } 291 } 292 293 if (i == addr_ct) { 294 MOZ_MTLOG(ML_ERROR, "No local addresses of the configured IP version"); 295 return R_INTERNAL; 296 } 297 298 int tries = 100; 299 while (tries--) { 300 // Bind on configured port (default 3478) 301 r = TryOpenListenSocket(&addrs[i], instance_port); 302 // We interpret R_ALREADY to mean the addr is probably in use. Try another. 303 // Otherwise, it either worked or it didn't, and we check below. 304 if (r != R_ALREADY) { 305 break; 306 } 307 ++instance_port; 308 } 309 310 if (r) { 311 return R_INTERNAL; 312 } 313 314 r = nr_socket_wrapped_create(listen_sock_, &send_sock_); 315 if (r) { 316 MOZ_MTLOG(ML_ERROR, "Couldn't create send socket"); 317 return R_INTERNAL; 318 } 319 320 r = nr_stun_server_ctx_create(const_cast<char*>("Test STUN server"), 321 &stun_server_); 322 if (r) { 323 MOZ_MTLOG(ML_ERROR, "Couldn't create STUN server"); 324 return R_INTERNAL; 325 } 326 327 // Cache the address and port. 328 char addr_string[INET6_ADDRSTRLEN]; 329 r = nr_transport_addr_get_addrstring(&addrs[i].addr, addr_string, 330 sizeof(addr_string)); 331 if (r) { 332 MOZ_MTLOG(ML_ERROR, 333 "Failed to convert listen addr to a string representation"); 334 return R_INTERNAL; 335 } 336 337 listen_addr_ = addr_string; 338 listen_port_ = instance_port; 339 340 return 0; 341 } 342 343 UniquePtr<TestStunServer> TestStunServer::Create(int address_family) { 344 NR_reg_init(); 345 346 UniquePtr<TestStunServer> server(new TestStunServer()); 347 348 if (server->Initialize(address_family)) return nullptr; 349 350 NR_SOCKET fd; 351 int r = nr_socket_getfd(server->listen_sock_, &fd); 352 if (r) { 353 MOZ_MTLOG(ML_ERROR, "Couldn't get fd"); 354 return nullptr; 355 } 356 357 NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, 358 server.get()); 359 360 return server; 361 } 362 363 void TestStunServer::ConfigurePort(uint16_t port) { instance_port = port; } 364 365 TestStunServer* TestStunServer::GetInstance(int address_family) { 366 switch (address_family) { 367 case AF_INET: 368 if (!instance) instance = Create(address_family).release(); 369 370 MOZ_ASSERT(instance); 371 return instance; 372 case AF_INET6: 373 if (!instance6) instance6 = Create(address_family).release(); 374 375 return instance6; 376 default: 377 MOZ_CRASH(); 378 } 379 } 380 381 void TestStunServer::ShutdownInstance() { 382 delete instance; 383 instance = nullptr; 384 delete instance6; 385 instance6 = nullptr; 386 } 387 388 struct DeferredStunOperation { 389 DeferredStunOperation(TestStunServer* server, const char* data, size_t len, 390 nr_transport_addr* addr, nr_socket* sock) 391 : server_(server), sock_(sock) { 392 buffer_.Copy(reinterpret_cast<const uint8_t*>(data), len); 393 nr_transport_addr_copy(&addr_, addr); 394 } 395 396 TestStunServer* server_; 397 MediaPacket buffer_; 398 nr_transport_addr addr_; 399 nr_socket* sock_; 400 }; 401 402 void TestStunServer::Process(const uint8_t* msg, size_t len, 403 nr_transport_addr* addr, nr_socket* sock) { 404 if (!sock) { 405 sock = send_sock_; 406 } 407 408 // Set the wrapped address so that the response goes to the right place. 409 nr_socket_wrapped_set_send_addr(sock, addr); 410 411 nr_stun_server_process_request( 412 stun_server_, sock, const_cast<char*>(reinterpret_cast<const char*>(msg)), 413 len, response_addr_ ? response_addr_ : addr, NR_STUN_AUTH_RULE_OPTIONAL); 414 } 415 416 void TestStunServer::process_cb(NR_SOCKET s, int how, void* cb_arg) { 417 DeferredStunOperation* op = static_cast<DeferredStunOperation*>(cb_arg); 418 op->server_->timer_handle_ = nullptr; 419 op->server_->Process(op->buffer_.data(), op->buffer_.len(), &op->addr_, 420 op->sock_); 421 422 delete op; 423 } 424 425 nr_socket* TestStunServer::GetReceivingSocket(NR_SOCKET s) { 426 return listen_sock_; 427 } 428 429 nr_socket* TestStunServer::GetSendingSocket(nr_socket* sock) { 430 return send_sock_; 431 } 432 433 void TestStunServer::readable_cb(NR_SOCKET s, int how, void* cb_arg) { 434 TestStunServer* server = static_cast<TestStunServer*>(cb_arg); 435 436 char message[max_stun_message_size]; 437 size_t message_len; 438 nr_transport_addr addr; 439 nr_socket* recv_sock = server->GetReceivingSocket(s); 440 if (!recv_sock) { 441 MOZ_MTLOG(ML_ERROR, "Failed to lookup receiving socket"); 442 return; 443 } 444 nr_socket* send_sock = server->GetSendingSocket(recv_sock); 445 446 /* Re-arm. */ 447 NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); 448 449 if (nr_socket_recvfrom(recv_sock, message, sizeof(message), &message_len, 0, 450 &addr)) { 451 MOZ_MTLOG(ML_ERROR, "Couldn't read STUN message"); 452 return; 453 } 454 455 MOZ_MTLOG(ML_DEBUG, "Received data of length " << message_len); 456 457 // If we have initial dropping set, check at this point. 458 std::string key(addr.as_string); 459 460 if (server->received_ct_.count(key) == 0) { 461 server->received_ct_[key] = 0; 462 } 463 464 ++server->received_ct_[key]; 465 466 if (!server->active_ || (server->received_ct_[key] <= server->initial_ct_)) { 467 MOZ_MTLOG(ML_DEBUG, "Dropping message #" << server->received_ct_[key] 468 << " from " << key); 469 return; 470 } 471 472 if (server->delay_ms_) { 473 NR_ASYNC_TIMER_SET(server->delay_ms_, process_cb, 474 new DeferredStunOperation(server, message, message_len, 475 &addr, send_sock), 476 &server->timer_handle_); 477 } else { 478 server->Process(reinterpret_cast<const uint8_t*>(message), message_len, 479 &addr, send_sock); 480 } 481 } 482 483 void TestStunServer::SetActive(bool active) { active_ = active; } 484 485 void TestStunServer::SetDelay(uint32_t delay_ms) { delay_ms_ = delay_ms; } 486 487 void TestStunServer::SetDropInitialPackets(uint32_t count) { 488 initial_ct_ = count; 489 } 490 491 nsresult TestStunServer::SetResponseAddr(nr_transport_addr* addr) { 492 delete response_addr_; 493 494 response_addr_ = new nr_transport_addr(); 495 496 int r = nr_transport_addr_copy(response_addr_, addr); 497 if (r) return NS_ERROR_FAILURE; 498 499 return NS_OK; 500 } 501 502 nsresult TestStunServer::SetResponseAddr(const std::string& addr, 503 uint16_t port) { 504 nr_transport_addr addr2; 505 506 int r = 507 nr_str_port_to_transport_addr(addr.c_str(), port, IPPROTO_UDP, &addr2); 508 if (r) return NS_ERROR_FAILURE; 509 510 return SetResponseAddr(&addr2); 511 } 512 513 void TestStunServer::Reset() { 514 delay_ms_ = 0; 515 if (timer_handle_) { 516 NR_async_timer_cancel(timer_handle_); 517 timer_handle_ = nullptr; 518 } 519 delete response_addr_; 520 response_addr_ = nullptr; 521 received_ct_.clear(); 522 } 523 524 // TestStunTcpServer 525 526 void TestStunTcpServer::ConfigurePort(uint16_t port) { instance_port = port; } 527 528 TestStunTcpServer* TestStunTcpServer::GetInstance(int address_family) { 529 switch (address_family) { 530 case AF_INET: 531 if (!instance) instance = Create(address_family).release(); 532 533 MOZ_ASSERT(instance); 534 return instance; 535 case AF_INET6: 536 if (!instance6) instance6 = Create(address_family).release(); 537 538 return instance6; 539 default: 540 MOZ_CRASH(); 541 } 542 } 543 544 void TestStunTcpServer::ShutdownInstance() { 545 delete instance; 546 instance = nullptr; 547 delete instance6; 548 instance6 = nullptr; 549 } 550 551 int TestStunTcpServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { 552 addr->addr.protocol = IPPROTO_TCP; 553 554 int r = SetInternalPort(addr, port); 555 556 if (r) return r; 557 558 nr_socket* sock; 559 if (nr_socket_local_create(nullptr, &addr->addr, &sock)) { 560 MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket"); 561 return R_ALREADY; 562 } 563 564 if (nr_socket_buffered_stun_create(sock, 2048, TURN_TCP_FRAMING, 565 &listen_sock_)) { 566 MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket"); 567 return R_ALREADY; 568 } 569 570 if (nr_socket_listen(listen_sock_, 10)) { 571 MOZ_MTLOG(ML_ERROR, "Couldn't listen on socket"); 572 return R_ALREADY; 573 } 574 575 return 0; 576 } 577 578 nr_socket* TestStunTcpServer::GetReceivingSocket(NR_SOCKET s) { 579 return connections_[s]; 580 } 581 582 nr_socket* TestStunTcpServer::GetSendingSocket(nr_socket* sock) { return sock; } 583 584 void TestStunTcpServer::accept_cb(NR_SOCKET s, int how, void* cb_arg) { 585 TestStunTcpServer* server = static_cast<TestStunTcpServer*>(cb_arg); 586 nr_socket *newsock, *bufsock, *wrapsock; 587 nr_transport_addr remote_addr; 588 NR_SOCKET fd; 589 590 /* rearm */ 591 NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, cb_arg); 592 593 /* accept */ 594 if (nr_socket_accept(server->listen_sock_, &remote_addr, &newsock)) { 595 MOZ_MTLOG(ML_ERROR, "Couldn't accept incoming tcp connection"); 596 return; 597 } 598 599 if (nr_socket_buffered_stun_create(newsock, 2048, TURN_TCP_FRAMING, 600 &bufsock)) { 601 MOZ_MTLOG(ML_ERROR, "Couldn't create connected tcp socket"); 602 nr_socket_destroy(&newsock); 603 return; 604 } 605 606 nr_socket_buffered_set_connected_to(bufsock, &remote_addr); 607 608 if (nr_socket_wrapped_create(bufsock, &wrapsock)) { 609 MOZ_MTLOG(ML_ERROR, "Couldn't wrap connected tcp socket"); 610 nr_socket_destroy(&bufsock); 611 return; 612 } 613 614 if (nr_socket_getfd(wrapsock, &fd)) { 615 MOZ_MTLOG(ML_ERROR, "Couldn't get fd from connected tcp socket"); 616 nr_socket_destroy(&wrapsock); 617 return; 618 } 619 620 server->connections_[fd] = wrapsock; 621 622 NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); 623 } 624 625 UniquePtr<TestStunTcpServer> TestStunTcpServer::Create(int address_family) { 626 NR_reg_init(); 627 628 UniquePtr<TestStunTcpServer> server(new TestStunTcpServer()); 629 630 if (server->Initialize(address_family)) { 631 return nullptr; 632 } 633 634 NR_SOCKET fd; 635 if (nr_socket_getfd(server->listen_sock_, &fd)) { 636 MOZ_MTLOG(ML_ERROR, "Couldn't get tcp fd"); 637 return nullptr; 638 } 639 640 NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, 641 server.get()); 642 643 return server; 644 } 645 646 TestStunTcpServer::~TestStunTcpServer() { 647 for (auto it = connections_.begin(); it != connections_.end();) { 648 NR_ASYNC_CANCEL(it->first, NR_ASYNC_WAIT_READ); 649 nr_socket_destroy(&it->second); 650 connections_.erase(it++); 651 } 652 } 653 654 } // namespace mozilla