summaryrefslogtreecommitdiffstats
path: root/media/mtransport
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /media/mtransport
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'media/mtransport')
-rw-r--r--media/mtransport/README59
-rw-r--r--media/mtransport/build/moz.build36
-rw-r--r--media/mtransport/common.build95
-rw-r--r--media/mtransport/databuffer.h74
-rw-r--r--media/mtransport/dtlsidentity.cpp214
-rw-r--r--media/mtransport/dtlsidentity.h70
-rw-r--r--media/mtransport/gonk_addrs.cpp170
-rw-r--r--media/mtransport/logging.h38
-rw-r--r--media/mtransport/m_cpp_utils.h28
-rw-r--r--media/mtransport/moz.build13
-rw-r--r--media/mtransport/nr_socket_prsock.cpp2299
-rw-r--r--media/mtransport/nr_socket_prsock.h411
-rw-r--r--media/mtransport/nr_timer.cpp265
-rw-r--r--media/mtransport/nricectx.cpp1017
-rw-r--r--media/mtransport/nricectx.h390
-rw-r--r--media/mtransport/nricectxhandler.cpp160
-rw-r--r--media/mtransport/nricectxhandler.h52
-rw-r--r--media/mtransport/nricemediastream.cpp610
-rw-r--r--media/mtransport/nricemediastream.h228
-rw-r--r--media/mtransport/nriceresolver.cpp236
-rw-r--r--media/mtransport/nriceresolver.h123
-rw-r--r--media/mtransport/nriceresolverfake.cpp192
-rw-r--r--media/mtransport/nriceresolverfake.h147
-rw-r--r--media/mtransport/nrinterfaceprioritizer.cpp268
-rw-r--r--media/mtransport/nrinterfaceprioritizer.h18
-rw-r--r--media/mtransport/rlogconnector.cpp191
-rw-r--r--media/mtransport/rlogconnector.h129
-rw-r--r--media/mtransport/runnable_utils.h258
-rw-r--r--media/mtransport/sigslot.h2859
-rw-r--r--media/mtransport/simpletokenbucket.cpp62
-rw-r--r--media/mtransport/simpletokenbucket.h55
-rw-r--r--media/mtransport/stun_socket_filter.cpp359
-rw-r--r--media/mtransport/stun_socket_filter.h36
-rw-r--r--media/mtransport/test/TestSyncRunnable.cpp61
-rw-r--r--media/mtransport/test/buffered_stun_socket_unittest.cpp296
-rw-r--r--media/mtransport/test/dummysocket.h233
-rw-r--r--media/mtransport/test/gtest_ringbuffer_dumper.h83
-rw-r--r--media/mtransport/test/gtest_utils.h203
-rw-r--r--media/mtransport/test/ice_unittest.cpp3934
-rw-r--r--media/mtransport/test/moz.build100
-rw-r--r--media/mtransport/test/mtransport_test_utils.h52
-rw-r--r--media/mtransport/test/multi_tcp_socket_unittest.cpp540
-rw-r--r--media/mtransport/test/nrappkit_unittest.cpp134
-rw-r--r--media/mtransport/test/proxy_tunnel_socket_unittest.cpp344
-rw-r--r--media/mtransport/test/rlogconnector_unittest.cpp264
-rw-r--r--media/mtransport/test/runnable_utils_unittest.cpp227
-rw-r--r--media/mtransport/test/sctp_unittest.cpp393
-rw-r--r--media/mtransport/test/simpletokenbucket_unittest.cpp107
-rw-r--r--media/mtransport/test/sockettransportservice_unittest.cpp208
-rw-r--r--media/mtransport/test/stunserver.cpp664
-rw-r--r--media/mtransport/test/stunserver.h123
-rw-r--r--media/mtransport/test/test_nr_socket_unittest.cpp878
-rw-r--r--media/mtransport/test/transport_unittests.cpp1344
-rw-r--r--media/mtransport/test/turn_unittest.cpp487
-rw-r--r--media/mtransport/test_nr_socket.cpp892
-rw-r--r--media/mtransport/test_nr_socket.h335
-rw-r--r--media/mtransport/testlib/moz.build17
-rw-r--r--media/mtransport/third_party/import.py40
-rw-r--r--media/mtransport/third_party/moz.build76
-rw-r--r--media/mtransport/third_party/nICEr/IMPORT_FILES71
-rw-r--r--media/mtransport/third_party/nICEr/README74
-rw-r--r--media/mtransport/third_party/nICEr/README_MOZILLA11
-rw-r--r--media/mtransport/third_party/nICEr/nicer.gyp254
-rw-r--r--media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c71
-rw-r--r--media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h52
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_candidate.c979
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_candidate.h121
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c684
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h95
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_codeword.h41
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_component.c1688
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_component.h104
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_ctx.c1057
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_ctx.h203
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_handler.h84
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c933
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h108
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_parser.c575
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c825
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h101
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_reg.h79
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_socket.c380
-rw-r--r--media/mtransport/third_party/nICEr/src/ice/ice_socket.h97
-rw-r--r--media/mtransport/third_party/nICEr/src/net/local_addr.c61
-rw-r--r--media/mtransport/third_party/nICEr/src/net/local_addr.h59
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c88
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h66
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c686
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h70
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_resolver.c85
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_resolver.h96
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_socket.c191
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_socket.h121
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_socket_local.h41
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c620
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h52
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c82
-rw-r--r--media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h63
-rw-r--r--media/mtransport/third_party/nICEr/src/net/transport_addr.c476
-rw-r--r--media/mtransport/third_party/nICEr/src/net/transport_addr.h103
-rw-r--r--media/mtransport/third_party/nICEr/src/net/transport_addr_reg.c233
-rw-r--r--media/mtransport/third_party/nICEr/src/net/transport_addr_reg.h46
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/addrs.c444
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/addrs.h43
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.c242
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.h57
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c610
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h64
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c198
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h48
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun.h216
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_build.c614
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_build.h147
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c824
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h199
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_codec.c1522
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_codec.h78
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_hint.c249
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_hint.h44
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_msg.c360
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_msg.h206
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_proc.c568
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_proc.h53
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_reg.h58
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c472
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.h82
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_util.c322
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/stun_util.h56
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c1056
-rw-r--r--media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h137
-rw-r--r--media/mtransport/third_party/nICEr/src/util/cb_args.c61
-rw-r--r--media/mtransport/third_party/nICEr/src/util/cb_args.h41
-rw-r--r--media/mtransport/third_party/nICEr/src/util/ice_util.c76
-rw-r--r--media/mtransport/third_party/nICEr/src/util/ice_util.h41
-rw-r--r--media/mtransport/third_party/nICEr/src/util/mbslen.c139
-rw-r--r--media/mtransport/third_party/nICEr/src/util/mbslen.h42
-rw-r--r--media/mtransport/third_party/nICEr/upstream.diff2449
-rw-r--r--media/mtransport/third_party/nrappkit/COPYRIGHT159
-rw-r--r--media/mtransport/third_party/nrappkit/IMPORT_FILES87
-rw-r--r--media/mtransport/third_party/nrappkit/README133
-rw-r--r--media/mtransport/third_party/nrappkit/README_MOZILLA22
-rw-r--r--media/mtransport/third_party/nrappkit/VERSION1
-rw-r--r--media/mtransport/third_party/nrappkit/nrappkit.gyp234
-rw-r--r--media/mtransport/third_party/nrappkit/src/event/async_timer.h54
-rw-r--r--media/mtransport/third_party/nrappkit/src/event/async_wait.h83
-rw-r--r--media/mtransport/third_party/nrappkit/src/event/async_wait_int.h62
-rw-r--r--media/mtransport/third_party/nrappkit/src/log/r_log.c694
-rw-r--r--media/mtransport/third_party/nrappkit/src/log/r_log.h85
-rw-r--r--media/mtransport/third_party/nrappkit/src/plugin/nr_plugin.h57
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/android/include/android_funcs.h62
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/android/include/csi_platform.h55
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/android/include/sys/ttycom.h38
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/android/port-impl.mk31
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h57
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/generic/include/sys/queue.h562
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h55
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/linux/include/linux_funcs.h62
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h38
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/linux/port-impl.mk31
-rw-r--r--media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h107
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/c2ru.c320
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/c2ru.h96
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/registry.c604
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/registry.h154
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/registry_int.h97
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/registry_local.c1168
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/registry_vtbl.h96
-rw-r--r--media/mtransport/third_party/nrappkit/src/registry/registrycb.c440
-rw-r--r--media/mtransport/third_party/nrappkit/src/share/nr_api.h51
-rw-r--r--media/mtransport/third_party/nrappkit/src/share/nr_common.h108
-rw-r--r--media/mtransport/third_party/nrappkit/src/share/nr_reg_keys.h167
-rw-r--r--media/mtransport/third_party/nrappkit/src/stats/nrstats.h118
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/byteorder.c76
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/byteorder.h47
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/hex.c110
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/hex.h47
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/assoc.h90
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/debug.c131
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/debug.h94
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.c541
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.h126
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_common.h100
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c177
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.h14
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_data.c249
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_data.h108
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_defaults.h91
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.c139
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.h127
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_includes.h98
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_list.c276
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_list.h106
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h137
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.c201
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.h101
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_replace.c110
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_thread.h68
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_time.c238
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h109
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/libekr/r_types.h213
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/p_buf.c217
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/p_buf.h72
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/util.c792
-rw-r--r--media/mtransport/third_party/nrappkit/src/util/util.h74
-rw-r--r--media/mtransport/third_party/nrappkit/upstream.diff391
-rw-r--r--media/mtransport/transportflow.cpp255
-rw-r--r--media/mtransport/transportflow.h143
-rw-r--r--media/mtransport/transportlayer.cpp52
-rw-r--r--media/mtransport/transportlayer.h118
-rw-r--r--media/mtransport/transportlayerdtls.cpp1262
-rw-r--r--media/mtransport/transportlayerdtls.h186
-rw-r--r--media/mtransport/transportlayerice.cpp227
-rw-r--r--media/mtransport/transportlayerice.h70
-rw-r--r--media/mtransport/transportlayerlog.cpp55
-rw-r--r--media/mtransport/transportlayerlog.h41
-rw-r--r--media/mtransport/transportlayerloopback.cpp131
-rw-r--r--media/mtransport/transportlayerloopback.h153
-rw-r--r--media/mtransport/transportlayerprsock.cpp116
-rw-r--r--media/mtransport/transportlayerprsock.h120
219 files changed, 61528 insertions, 0 deletions
diff --git a/media/mtransport/README b/media/mtransport/README
new file mode 100644
index 000000000..f25ae6e38
--- /dev/null
+++ b/media/mtransport/README
@@ -0,0 +1,59 @@
+This is a generic media transport system for WebRTC.
+
+The basic model is that you have a TransportFlow which contains a
+series of TransportLayers, each of which gets an opportunity to
+manipulate data up and down the stack (think SysV STREAMS or a
+standard networking stack). You can also address individual
+sublayers to manipulate them or to bypass reading and writing
+at an upper layer; WebRTC uses this to implement DTLS-SRTP.
+
+
+DATAFLOW MODEL
+Unlike the existing nsSocket I/O system, this is a push rather
+than a pull system. Clients of the interface do writes downward
+with SendPacket() and receive notification of incoming packets
+via callbacks registed via sigslot.h. It is the responsibility
+of the bottom layer (or any other layer which needs to reference
+external events) to arrange for that somehow; typically by
+using nsITimer or the SocketTansportService.
+
+This sort of push model is a much better fit for the demands
+of WebRTC, expecially because ICE contexts span multiple
+network transports.
+
+
+THREADING MODEL
+There are no thread locks. It is the responsibility of the caller to
+arrange that any given TransportLayer/TransportFlow is only
+manipulated in one thread at once. One good way to do this is to run
+everything on the STS thread. Many of the existing layer implementations
+(TransportLayerPrsock, TransportLayerIce, TransportLayerLoopback)
+already run on STS so in those cases you must run on STS, though
+you can do setup on the main thread and then activate them on the
+STS.
+
+
+EXISTING TRANSPORT LAYERS
+The following transport layers are currently implemented:
+
+* DTLS -- a wrapper around NSS's DTLS [RFC 6347] stack
+* ICE -- a wrapper around the nICEr ICE [RFC 5245] stack.
+* Prsock -- a wrapper around NSPR sockets
+* Loopback -- a loopback IO mechanism
+* Logging -- a passthrough that just logs its data
+
+The last three are primarily for debugging.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/media/mtransport/build/moz.build b/media/mtransport/build/moz.build
new file mode 100644
index 000000000..2d6e01ee4
--- /dev/null
+++ b/media/mtransport/build/moz.build
@@ -0,0 +1,36 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+EXPORTS.mtransport += [
+ '../dtlsidentity.h',
+ '../m_cpp_utils.h',
+ '../nricectx.h',
+ '../nricemediastream.h',
+ '../nriceresolverfake.h',
+ '../rlogconnector.h',
+ '../runnable_utils.h',
+ '../sigslot.h',
+ '../simpletokenbucket.h',
+ '../stun_socket_filter.h',
+ '../transportflow.h',
+ '../transportlayer.h',
+ '../transportlayerdtls.h',
+ '../transportlayerice.h',
+ '../transportlayerlog.h',
+ '../transportlayerloopback.h',
+ '../transportlayerprsock.h',
+]
+
+include('../common.build')
+
+# These files cannot be built in unified mode because of the redefinition of
+# getLogModule, UNIMPLEMENTED, nr_socket_long_term_violation_time,
+# nr_socket_short_term_violation_time.
+SOURCES += mtransport_cppsrcs
+
+FINAL_LIBRARY = 'xul'
diff --git a/media/mtransport/common.build b/media/mtransport/common.build
new file mode 100644
index 000000000..7576ab4fc
--- /dev/null
+++ b/media/mtransport/common.build
@@ -0,0 +1,95 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+mtransport_lcppsrcs = [
+ 'dtlsidentity.cpp',
+ 'nr_socket_prsock.cpp',
+ 'nr_timer.cpp',
+ 'nricectx.cpp',
+ 'nricectxhandler.cpp',
+ 'nricemediastream.cpp',
+ 'nriceresolver.cpp',
+ 'nriceresolverfake.cpp',
+ 'nrinterfaceprioritizer.cpp',
+ 'rlogconnector.cpp',
+ 'simpletokenbucket.cpp',
+ 'stun_socket_filter.cpp',
+ 'test_nr_socket.cpp',
+ 'transportflow.cpp',
+ 'transportlayer.cpp',
+ 'transportlayerdtls.cpp',
+ 'transportlayerice.cpp',
+ 'transportlayerlog.cpp',
+ 'transportlayerloopback.cpp',
+ 'transportlayerprsock.cpp',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ mtransport_lcppsrcs += [
+ 'gonk_addrs.cpp',
+ ]
+
+mtransport_cppsrcs = [
+ '/media/mtransport/%s' % s for s in sorted(mtransport_lcppsrcs)
+]
+
+LOCAL_INCLUDES += [
+ '/media/mtransport/',
+ '/media/mtransport/third_party/',
+ '/media/mtransport/third_party/nICEr/src/crypto',
+ '/media/mtransport/third_party/nICEr/src/ice',
+ '/media/mtransport/third_party/nICEr/src/net',
+ '/media/mtransport/third_party/nICEr/src/stun',
+ '/media/mtransport/third_party/nICEr/src/util',
+ '/media/mtransport/third_party/nrappkit/src/event',
+ '/media/mtransport/third_party/nrappkit/src/log',
+ '/media/mtransport/third_party/nrappkit/src/plugin',
+ '/media/mtransport/third_party/nrappkit/src/port/generic/include',
+ '/media/mtransport/third_party/nrappkit/src/registry',
+ '/media/mtransport/third_party/nrappkit/src/share',
+ '/media/mtransport/third_party/nrappkit/src/stats',
+ '/media/mtransport/third_party/nrappkit/src/util/libekr',
+]
+
+if CONFIG['OS_TARGET'] in ['Darwin', 'DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']:
+ if CONFIG['OS_TARGET'] == 'Darwin':
+ DEFINES['DARWIN'] = True
+ else:
+ DEFINES['BSD'] = True
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/darwin/include',
+ ]
+elif CONFIG['OS_TARGET'] == 'Linux':
+ DEFINES['LINUX'] = True
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/linux/include',
+ ]
+elif CONFIG['OS_TARGET'] == 'Android':
+ DEFINES['LINUX'] = True
+ DEFINES['ANDROID'] = True
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/android/include',
+ ]
+elif CONFIG['OS_TARGET'] == 'WINNT':
+ DEFINES['WIN'] = True
+ # for stun.h
+ DEFINES['WIN32'] = True
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/win32/include',
+ ]
+
+for var in ('HAVE_STRDUP', 'NR_SOCKET_IS_VOID_PTR'):
+ DEFINES[var] = True
+
+DEFINES['R_DEFINED_INT2'] = 'int16_t'
+DEFINES['R_DEFINED_UINT2'] = 'uint16_t'
+DEFINES['R_DEFINED_INT4'] = 'int32_t'
+DEFINES['R_DEFINED_UINT4'] = 'uint32_t'
+DEFINES['R_DEFINED_INT8'] = 'int64_t'
+DEFINES['R_DEFINED_UINT8'] = 'uint64_t'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/media/mtransport/databuffer.h b/media/mtransport/databuffer.h
new file mode 100644
index 000000000..fb60ecb98
--- /dev/null
+++ b/media/mtransport/databuffer.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef databuffer_h__
+#define databuffer_h__
+#include <algorithm>
+#include <mozilla/UniquePtr.h>
+#include <m_cpp_utils.h>
+#include <nsISupportsImpl.h>
+
+namespace mozilla {
+
+class DataBuffer {
+ public:
+ DataBuffer() : data_(nullptr), len_(0), capacity_(0) {}
+ DataBuffer(const uint8_t *data, size_t len) {
+ Assign(data, len, len);
+ }
+ DataBuffer(const uint8_t *data, size_t len, size_t capacity) {
+ Assign(data, len, capacity);
+ }
+
+ // to ensure extra space for expansion
+ void Assign(const uint8_t *data, size_t len, size_t capacity) {
+ MOZ_RELEASE_ASSERT(len <= capacity);
+ Allocate(capacity); // sets len_ = capacity
+ memcpy(static_cast<void *>(data_.get()),
+ static_cast<const void *>(data), len);
+ len_ = len;
+ }
+
+ void Allocate(size_t capacity) {
+ data_.reset(new uint8_t[capacity ? capacity : 1]); // Don't depend on new [0].
+ len_ = capacity_ = capacity;
+ }
+
+ void EnsureCapacity(size_t capacity) {
+ if (capacity_ < capacity) {
+ uint8_t *new_data = new uint8_t[ capacity ? capacity : 1];
+ memcpy(static_cast<void *>(new_data),
+ static_cast<const void *>(data_.get()), len_);
+ data_.reset(new_data); // after copying! Deletes old data
+ capacity_ = capacity;
+ }
+ }
+
+ // used when something writes to the buffer (having checked
+ // capacity() or used EnsureCapacity()) and increased the length.
+ void SetLength(size_t len) {
+ MOZ_RELEASE_ASSERT(len <= capacity_);
+ len_ = len;
+ }
+
+ const uint8_t *data() const { return data_.get(); }
+ uint8_t *data() { return data_.get(); }
+ size_t len() const { return len_; }
+ size_t capacity() const { return capacity_; }
+
+private:
+ UniquePtr<uint8_t[]> data_;
+ size_t len_;
+ size_t capacity_;
+
+ DISALLOW_COPY_ASSIGN(DataBuffer);
+};
+
+}
+
+#endif
diff --git a/media/mtransport/dtlsidentity.cpp b/media/mtransport/dtlsidentity.cpp
new file mode 100644
index 000000000..2eda501cf
--- /dev/null
+++ b/media/mtransport/dtlsidentity.cpp
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "dtlsidentity.h"
+
+#include "cert.h"
+#include "cryptohi.h"
+#include "keyhi.h"
+#include "nsError.h"
+#include "pk11pub.h"
+#include "sechash.h"
+#include "ssl.h"
+
+#include "mozilla/Sprintf.h"
+
+namespace mozilla {
+
+RefPtr<DtlsIdentity> DtlsIdentity::Generate() {
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return nullptr;
+ }
+
+ uint8_t random_name[16];
+
+ SECStatus rv = PK11_GenerateRandomOnSlot(slot, random_name,
+ sizeof(random_name));
+ if (rv != SECSuccess)
+ return nullptr;
+
+ std::string name;
+ char chunk[3];
+ for (size_t i = 0; i < sizeof(random_name); ++i) {
+ SprintfLiteral(chunk, "%.2x", random_name[i]);
+ name += chunk;
+ }
+
+ std::string subject_name_string = "CN=" + name;
+ ScopedCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str()));
+ if (!subject_name) {
+ return nullptr;
+ }
+
+ unsigned char paramBuf[12]; // OIDs are small
+ SECItem ecdsaParams = { siBuffer, paramBuf, sizeof(paramBuf) };
+ SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
+ if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) {
+ return nullptr;
+ }
+ ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID;
+ ecdsaParams.data[1] = oidData->oid.len;
+ memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len);
+ ecdsaParams.len = oidData->oid.len + 2;
+
+ ScopedSECKEYPrivateKey private_key;
+ ScopedSECKEYPublicKey public_key;
+ SECKEYPublicKey *pubkey;
+
+ private_key =
+ PK11_GenerateKeyPair(slot,
+ CKM_EC_KEY_PAIR_GEN, &ecdsaParams, &pubkey,
+ PR_FALSE, PR_TRUE, nullptr);
+ if (private_key == nullptr)
+ return nullptr;
+ public_key = pubkey;
+
+ ScopedCERTSubjectPublicKeyInfo spki(
+ SECKEY_CreateSubjectPublicKeyInfo(pubkey));
+ if (!spki) {
+ return nullptr;
+ }
+
+ ScopedCERTCertificateRequest certreq(
+ CERT_CreateCertificateRequest(subject_name, spki, nullptr));
+ if (!certreq) {
+ return nullptr;
+ }
+
+ // From 1 day before todayto 30 days after.
+ // This is a sort of arbitrary range designed to be valid
+ // now with some slack in case the other side expects
+ // some before expiry.
+ //
+ // Note: explicit casts necessary to avoid
+ // warning C4307: '*' : integral constant overflow
+ static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
+ * PRTime(60) // sec
+ * PRTime(60) // min
+ * PRTime(24); // hours
+ PRTime now = PR_Now();
+ PRTime notBefore = now - oneDay;
+ PRTime notAfter = now + (PRTime(30) * oneDay);
+
+ ScopedCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
+ if (!validity) {
+ return nullptr;
+ }
+
+ unsigned long serial;
+ // Note: This serial in principle could collide, but it's unlikely
+ rv = PK11_GenerateRandomOnSlot(slot,
+ reinterpret_cast<unsigned char *>(&serial),
+ sizeof(serial));
+ if (rv != SECSuccess) {
+ return nullptr;
+ }
+
+ ScopedCERTCertificate certificate(
+ CERT_CreateCertificate(serial, subject_name, validity, certreq));
+ if (!certificate) {
+ return nullptr;
+ }
+
+ PLArenaPool *arena = certificate->arena;
+
+ rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
+ SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0);
+ if (rv != SECSuccess)
+ return nullptr;
+
+ // Set version to X509v3.
+ *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3;
+ certificate->version.len = 1;
+
+ SECItem innerDER;
+ innerDER.len = 0;
+ innerDER.data = nullptr;
+
+ if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate,
+ SEC_ASN1_GET(CERT_CertificateTemplate))) {
+ return nullptr;
+ }
+
+ SECItem *signedCert = PORT_ArenaZNew(arena, SECItem);
+ if (!signedCert) {
+ return nullptr;
+ }
+
+ rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
+ private_key,
+ SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
+ if (rv != SECSuccess) {
+ return nullptr;
+ }
+ certificate->derCert = *signedCert;
+
+ RefPtr<DtlsIdentity> identity =
+ new DtlsIdentity(private_key.forget(), certificate.forget(), ssl_kea_ecdh);
+ return identity.forget();
+}
+
+const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256";
+
+nsresult DtlsIdentity::ComputeFingerprint(const std::string algorithm,
+ uint8_t *digest,
+ size_t size,
+ size_t *digest_length) const {
+ const UniqueCERTCertificate& c = cert();
+ MOZ_ASSERT(c);
+
+ return ComputeFingerprint(c, algorithm, digest, size, digest_length);
+}
+
+nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert,
+ const std::string algorithm,
+ uint8_t *digest,
+ size_t size,
+ size_t *digest_length) {
+ MOZ_ASSERT(cert);
+
+ HASH_HashType ht;
+
+ if (algorithm == "sha-1") {
+ ht = HASH_AlgSHA1;
+ } else if (algorithm == "sha-224") {
+ ht = HASH_AlgSHA224;
+ } else if (algorithm == "sha-256") {
+ ht = HASH_AlgSHA256;
+ } else if (algorithm == "sha-384") {
+ ht = HASH_AlgSHA384;
+ } else if (algorithm == "sha-512") {
+ ht = HASH_AlgSHA512;
+ } else {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const SECHashObject *ho = HASH_GetHashObject(ht);
+ MOZ_ASSERT(ho);
+ if (!ho) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ MOZ_ASSERT(ho->length >= 20); // Double check
+
+ if (size < ho->length) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ SECStatus rv = HASH_HashBuf(ho->type, digest,
+ cert->derCert.data,
+ cert->derCert.len);
+ if (rv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *digest_length = ho->length;
+
+ return NS_OK;
+}
+
+} // close namespace
diff --git a/media/mtransport/dtlsidentity.h b/media/mtransport/dtlsidentity.h
new file mode 100644
index 000000000..8dd742ce5
--- /dev/null
+++ b/media/mtransport/dtlsidentity.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef dtls_identity_h__
+#define dtls_identity_h__
+
+#include <string>
+
+#include "m_cpp_utils.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+#include "sslt.h"
+#include "ScopedNSSTypes.h"
+
+// All code in this module requires NSS to be live.
+// Callers must initialize NSS and implement the nsNSSShutdownObject
+// protocol.
+namespace mozilla {
+
+class DtlsIdentity final {
+ public:
+ // This constructor takes ownership of privkey and cert.
+ DtlsIdentity(SECKEYPrivateKey *privkey,
+ CERTCertificate *cert,
+ SSLKEAType authType)
+ : private_key_(privkey), cert_(cert), auth_type_(authType) {}
+
+ // This is only for use in tests, or for external linkage. It makes a (bad)
+ // instance of this class.
+ static RefPtr<DtlsIdentity> Generate();
+
+ // These don't create copies or transfer ownership. If you want these to live
+ // on, make a copy.
+ const UniqueCERTCertificate& cert() const { return cert_; }
+ SECKEYPrivateKey *privkey() const { return private_key_; }
+ // Note: this uses SSLKEAType because that is what the libssl API requires.
+ // This is a giant confusing mess, but libssl indexes certificates based on a
+ // key exchange type, not authentication type (as you might have reasonably
+ // expected).
+ SSLKEAType auth_type() const { return auth_type_; }
+
+ nsresult ComputeFingerprint(const std::string algorithm,
+ uint8_t *digest,
+ size_t size,
+ size_t *digest_length) const;
+ static nsresult ComputeFingerprint(const UniqueCERTCertificate& cert,
+ const std::string algorithm,
+ uint8_t *digest,
+ size_t size,
+ size_t *digest_length);
+
+ static const std::string DEFAULT_HASH_ALGORITHM;
+ enum {
+ HASH_ALGORITHM_MAX_LENGTH = 64
+ };
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DtlsIdentity)
+
+ private:
+ ~DtlsIdentity() {}
+ DISALLOW_COPY_ASSIGN(DtlsIdentity);
+
+ ScopedSECKEYPrivateKey private_key_;
+ UniqueCERTCertificate cert_;
+ SSLKEAType auth_type_;
+};
+} // close namespace
+#endif
diff --git a/media/mtransport/gonk_addrs.cpp b/media/mtransport/gonk_addrs.cpp
new file mode 100644
index 000000000..1c69b711e
--- /dev/null
+++ b/media/mtransport/gonk_addrs.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+extern "C" {
+#include <arpa/inet.h>
+#include "r_types.h"
+#include "stun.h"
+#include "addrs.h"
+}
+
+#include <vector>
+#include <string>
+#include "nsINetworkInterface.h"
+#include "nsINetworkInterfaceListService.h"
+#include "runnable_utils.h"
+#include "nsCOMPtr.h"
+#include "nsMemory.h"
+#include "nsThreadUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/SyncRunnable.h"
+
+namespace {
+struct NetworkInterface {
+ struct sockaddr_in addr;
+ std::string name;
+ // See NR_INTERFACE_TYPE_* in nICEr/src/net/local_addrs.h
+ int type;
+};
+
+nsresult
+GetInterfaces(std::vector<NetworkInterface>* aInterfaces)
+{
+ MOZ_ASSERT(aInterfaces);
+
+ // Obtain network interfaces from network manager.
+ nsresult rv;
+ nsCOMPtr<nsINetworkInterfaceListService> listService =
+ do_GetService("@mozilla.org/network/interface-list-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t flags =
+ nsINetworkInterfaceListService::LIST_NOT_INCLUDE_SUPL_INTERFACES |
+ nsINetworkInterfaceListService::LIST_NOT_INCLUDE_MMS_INTERFACES |
+ nsINetworkInterfaceListService::LIST_NOT_INCLUDE_IMS_INTERFACES |
+ nsINetworkInterfaceListService::LIST_NOT_INCLUDE_DUN_INTERFACES |
+ nsINetworkInterfaceListService::LIST_NOT_INCLUDE_FOTA_INTERFACES;
+ nsCOMPtr<nsINetworkInterfaceList> networkList;
+ NS_ENSURE_SUCCESS(listService->GetDataInterfaceList(flags,
+ getter_AddRefs(networkList)),
+ NS_ERROR_FAILURE);
+
+ // Translate nsINetworkInterfaceList to NetworkInterface.
+ int32_t listLength;
+ NS_ENSURE_SUCCESS(networkList->GetNumberOfInterface(&listLength),
+ NS_ERROR_FAILURE);
+ aInterfaces->clear();
+
+ for (int32_t i = 0; i < listLength; i++) {
+ nsCOMPtr<nsINetworkInfo> info;
+ if (NS_FAILED(networkList->GetInterfaceInfo(i, getter_AddRefs(info)))) {
+ continue;
+ }
+
+ char16_t **ips = nullptr;
+ uint32_t *prefixs = nullptr;
+ uint32_t count = 0;
+ bool isAddressGot = false;
+ NetworkInterface interface;
+ memset(&(interface.addr), 0, sizeof(interface.addr));
+ interface.addr.sin_family = AF_INET;
+
+ if (NS_FAILED(info->GetAddresses(&ips, &prefixs, &count))) {
+ continue;
+ }
+
+ for (uint32_t j = 0; j < count; j++) {
+ nsAutoString ip;
+
+ ip.Assign(ips[j]);
+ if (inet_pton(AF_INET, NS_ConvertUTF16toUTF8(ip).get(),
+ &(interface.addr.sin_addr.s_addr)) == 1) {
+ isAddressGot = true;
+ break;
+ }
+ }
+
+ free(prefixs);
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, ips);
+
+ if (!isAddressGot) {
+ continue;
+ }
+
+ nsAutoString ifaceName;
+ if (NS_FAILED(info->GetName(ifaceName))) {
+ continue;
+ }
+ interface.name = NS_ConvertUTF16toUTF8(ifaceName).get();
+
+ int32_t type;
+ if (NS_FAILED(info->GetType(&type))) {
+ continue;
+ }
+ switch (type) {
+ case nsINetworkInfo::NETWORK_TYPE_WIFI:
+ interface.type = NR_INTERFACE_TYPE_WIFI;
+ break;
+ case nsINetworkInfo::NETWORK_TYPE_MOBILE:
+ interface.type = NR_INTERFACE_TYPE_MOBILE;
+ break;
+ }
+
+ aInterfaces->push_back(interface);
+ }
+ return NS_OK;
+}
+} // anonymous namespace
+
+int
+nr_stun_get_addrs(nr_local_addr aAddrs[], int aMaxAddrs,
+ int aDropLoopback, int aDropLinkLocal, int* aCount)
+{
+ nsresult rv;
+ int r;
+
+ // Get network interface list.
+ std::vector<NetworkInterface> interfaces;
+ nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+ mozilla::SyncRunnable::DispatchToThread(
+ mainThread.get(),
+ mozilla::WrapRunnableNMRet(&rv, &GetInterfaces, &interfaces),
+ false);
+ if (NS_FAILED(rv)) {
+ return R_FAILED;
+ }
+
+ // Translate to nr_transport_addr.
+ int32_t n = 0;
+ size_t num_interface = std::min(interfaces.size(), (size_t)aMaxAddrs);
+ for (size_t i = 0; i < num_interface; ++i) {
+ NetworkInterface &interface = interfaces[i];
+ if (nr_sockaddr_to_transport_addr((sockaddr*)&(interface.addr),
+ IPPROTO_UDP, 0, &(aAddrs[n].addr))) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Problem transforming address");
+ return R_FAILED;
+ }
+ strlcpy(aAddrs[n].addr.ifname, interface.name.c_str(),
+ sizeof(aAddrs[n].addr.ifname));
+ aAddrs[n].interface.type = interface.type;
+ aAddrs[n].interface.estimated_speed = 0;
+ n++;
+ }
+
+ *aCount = n;
+ r = nr_stun_remove_duplicate_addrs(aAddrs, aDropLoopback, aDropLinkLocal, aCount);
+ if (r != 0) {
+ return r;
+ }
+
+ for (int i = 0; i < *aCount; ++i) {
+ char typestr[100];
+ nr_local_addr_fmt_info_string(aAddrs + i, typestr, sizeof(typestr));
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s, type: %s\n",
+ i, aAddrs[i].addr.as_string, aAddrs[i].addr.ifname, typestr);
+ }
+
+ return 0;
+}
diff --git a/media/mtransport/logging.h b/media/mtransport/logging.h
new file mode 100644
index 000000000..1a2b71cf9
--- /dev/null
+++ b/media/mtransport/logging.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef logging_h__
+#define logging_h__
+
+#include <sstream>
+#include "mozilla/Logging.h"
+
+#define ML_ERROR mozilla::LogLevel::Error
+#define ML_WARNING mozilla::LogLevel::Warning
+#define ML_NOTICE mozilla::LogLevel::Info
+#define ML_INFO mozilla::LogLevel::Debug
+#define ML_DEBUG mozilla::LogLevel::Verbose
+
+#define MOZ_MTLOG_MODULE(n) \
+ static PRLogModuleInfo* getLogModule() { \
+ static PRLogModuleInfo* log; \
+ if (!log) \
+ log = PR_NewLogModule(n); \
+ return log; \
+ }
+
+#define MOZ_MTLOG(level, b) \
+ do { \
+ if (MOZ_LOG_TEST(getLogModule(), level)) { \
+ std::stringstream str; \
+ str << b; \
+ MOZ_LOG(getLogModule(), level, ("%s", str.str().c_str())); \
+ } \
+ } while(0)
+
+#endif // logging_h__
diff --git a/media/mtransport/m_cpp_utils.h b/media/mtransport/m_cpp_utils.h
new file mode 100644
index 000000000..f119b36e5
--- /dev/null
+++ b/media/mtransport/m_cpp_utils.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef m_cpp_utils_h__
+#define m_cpp_utils_h__
+
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+
+#define DISALLOW_ASSIGNMENT(T) \
+ void operator=(const T& other) = delete
+
+#define DISALLOW_COPY(T) \
+ T(const T& other) = delete
+
+
+#define DISALLOW_COPY_ASSIGN(T) \
+ DISALLOW_COPY(T); \
+ DISALLOW_ASSIGNMENT(T)
+
+} // close namespace
+#endif
diff --git a/media/mtransport/moz.build b/media/mtransport/moz.build
new file mode 100644
index 000000000..bd1475721
--- /dev/null
+++ b/media/mtransport/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+DIRS += [
+ '/media/mtransport/third_party',
+ '/media/mtransport/build',
+ '/media/mtransport/testlib',
+]
diff --git a/media/mtransport/nr_socket_prsock.cpp b/media/mtransport/nr_socket_prsock.cpp
new file mode 100644
index 000000000..f22229df8
--- /dev/null
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -0,0 +1,2299 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+Modified version of nr_socket_local, adapted for NSPR
+*/
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+Original code from nICEr and nrappkit.
+
+nICEr copyright:
+
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+nrappkit copyright:
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Thu Dec 20 20:14:49 2001
+*/
+
+#include <csi_platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <string>
+
+#include "nspr.h"
+#include "prerror.h"
+#include "prio.h"
+#include "prnetdb.h"
+
+#include "mozilla/net/DNS.h"
+#include "nsCOMPtr.h"
+#include "nsASocketHandler.h"
+#include "nsISocketTransportService.h"
+#include "nsNetCID.h"
+#include "nsISupportsImpl.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsXPCOM.h"
+#include "nsXULAppAPI.h"
+#include "runnable_utils.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsTArray.h"
+#include "mozilla/dom/TCPSocketBinding.h"
+#include "nsITCPSocketCallback.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsISocketFilter.h"
+
+#ifdef XP_WIN
+#include "mozilla/WindowsVersion.h"
+#endif
+
+#if defined(MOZILLA_INTERNAL_API)
+// csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING
+#ifdef LOG_INFO
+#define LOG_TEMP_INFO LOG_INFO
+#undef LOG_INFO
+#endif
+#ifdef LOG_WARNING
+#define LOG_TEMP_WARNING LOG_WARNING
+#undef LOG_WARNING
+#endif
+#if defined(LOG_DEBUG)
+#define LOG_TEMP_DEBUG LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+#undef strlcpy
+
+#include "mozilla/dom/network/TCPSocketChild.h"
+
+#ifdef LOG_TEMP_INFO
+#define LOG_INFO LOG_TEMP_INFO
+#endif
+#ifdef LOG_TEMP_WARNING
+#define LOG_WARNING LOG_TEMP_WARNING
+#endif
+
+#ifdef LOG_TEMP_DEBUG
+#define LOG_DEBUG LOG_TEMP_DEBUG
+#endif
+#ifdef XP_WIN
+#ifdef LOG_DEBUG
+#undef LOG_DEBUG
+#endif
+// cloned from csi_platform.h. Win32 doesn't like how we hide symbols
+#define LOG_DEBUG 7
+#endif
+#endif
+
+
+extern "C" {
+#include "nr_api.h"
+#include "async_wait.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_hint.h"
+}
+#include "nr_socket_prsock.h"
+#include "simpletokenbucket.h"
+#include "test_nr_socket.h"
+
+// Implement the nsISupports ref counting
+namespace mozilla {
+
+#if defined(MOZILLA_INTERNAL_API)
+class SingletonThreadHolder final
+{
+private:
+ ~SingletonThreadHolder()
+ {
+ r_log(LOG_GENERIC,LOG_DEBUG,"Deleting SingletonThreadHolder");
+ MOZ_ASSERT(!mThread, "SingletonThreads should be Released and shut down before exit!");
+ if (mThread) {
+ mThread->Shutdown();
+ mThread = nullptr;
+ }
+ }
+
+ DISALLOW_COPY_ASSIGN(SingletonThreadHolder);
+
+public:
+ // Must be threadsafe for StaticRefPtr/ClearOnShutdown
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SingletonThreadHolder)
+
+ explicit SingletonThreadHolder(const nsCSubstring& aName)
+ : mName(aName)
+ {
+ mParentThread = NS_GetCurrentThread();
+ }
+
+ nsIThread* GetThread() {
+ return mThread;
+ }
+
+ /*
+ * Keep track of how many instances are using a SingletonThreadHolder.
+ * When no one is using it, shut it down
+ */
+ MozExternalRefCountType AddUse()
+ {
+ MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
+ MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
+ nsrefcnt count = ++mUseCount;
+ if (count == 1) {
+ // idle -> in-use
+ nsresult rv = NS_NewThread(getter_AddRefs(mThread));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mThread,
+ "Should successfully create mtransport I/O thread");
+ NS_SetThreadName(mThread, mName);
+ r_log(LOG_GENERIC,LOG_DEBUG,"Created wrapped SingletonThread %p",
+ mThread.get());
+ }
+ r_log(LOG_GENERIC,LOG_DEBUG,"AddUse: %lu", (unsigned long) count);
+ return count;
+ }
+
+ MozExternalRefCountType ReleaseUse()
+ {
+ MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
+ nsrefcnt count = --mUseCount;
+ MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
+ if (count == 0) {
+ // in-use -> idle -- no one forcing it to remain instantiated
+ r_log(LOG_GENERIC,LOG_DEBUG,"Shutting down wrapped SingletonThread %p",
+ mThread.get());
+ mThread->Shutdown();
+ mThread = nullptr;
+ // It'd be nice to use a timer instead... But be careful of
+ // xpcom-shutdown-threads in that case
+ }
+ r_log(LOG_GENERIC,LOG_DEBUG,"ReleaseUse: %lu", (unsigned long) count);
+ return count;
+ }
+
+private:
+ nsCString mName;
+ nsAutoRefCnt mUseCount;
+ nsCOMPtr<nsIThread> mParentThread;
+ nsCOMPtr<nsIThread> mThread;
+};
+
+static StaticRefPtr<SingletonThreadHolder> sThread;
+
+static void ClearSingletonOnShutdown()
+{
+ ClearOnShutdown(&sThread);
+}
+#endif
+
+static nsIThread* GetIOThreadAndAddUse_s()
+{
+ // Always runs on STS thread!
+#if defined(MOZILLA_INTERNAL_API)
+ // We need to safely release this on shutdown to avoid leaks
+ if (!sThread) {
+ sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
+ NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
+ }
+ // Mark that we're using the shared thread and need it to stick around
+ sThread->AddUse();
+ return sThread->GetThread();
+#else
+ static nsCOMPtr<nsIThread> sThread;
+ if (!sThread) {
+ (void) NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
+ }
+ return sThread;
+#endif
+}
+
+NrSocketIpc::NrSocketIpc(nsIEventTarget *aThread)
+ : io_thread_(aThread)
+{}
+
+static TimeStamp nr_socket_short_term_violation_time;
+static TimeStamp nr_socket_long_term_violation_time;
+
+TimeStamp NrSocketBase::short_term_violation_time() {
+ return nr_socket_short_term_violation_time;
+}
+
+TimeStamp NrSocketBase::long_term_violation_time() {
+ return nr_socket_long_term_violation_time;
+}
+
+// NrSocketBase implementation
+// async_event APIs
+int NrSocketBase::async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line) {
+ uint16_t flag;
+
+ switch (how) {
+ case NR_ASYNC_WAIT_READ:
+ flag = PR_POLL_READ;
+ break;
+ case NR_ASYNC_WAIT_WRITE:
+ flag = PR_POLL_WRITE;
+ break;
+ default:
+ return R_BAD_ARGS;
+ }
+
+ cbs_[how] = cb;
+ cb_args_[how] = cb_arg;
+ poll_flags_ |= flag;
+
+ return 0;
+}
+
+int NrSocketBase::cancel(int how) {
+ uint16_t flag;
+
+ switch (how) {
+ case NR_ASYNC_WAIT_READ:
+ flag = PR_POLL_READ;
+ break;
+ case NR_ASYNC_WAIT_WRITE:
+ flag = PR_POLL_WRITE;
+ break;
+ default:
+ return R_BAD_ARGS;
+ }
+
+ poll_flags_ &= ~flag;
+
+ return 0;
+}
+
+void NrSocketBase::fire_callback(int how) {
+ // This can't happen unless we are armed because we only set
+ // the flags if we are armed
+ MOZ_ASSERT(cbs_[how]);
+
+ // Now cancel so that we need to be re-armed. Note that
+ // the re-arming probably happens in the callback we are
+ // about to fire.
+ cancel(how);
+
+ cbs_[how](this, how, cb_args_[how]);
+}
+
+// NrSocket implementation
+NS_IMPL_ISUPPORTS0(NrSocket)
+
+
+// The nsASocket callbacks
+void NrSocket::OnSocketReady(PRFileDesc *fd, int16_t outflags) {
+ if (outflags & PR_POLL_READ & poll_flags())
+ fire_callback(NR_ASYNC_WAIT_READ);
+ if (outflags & PR_POLL_WRITE & poll_flags())
+ fire_callback(NR_ASYNC_WAIT_WRITE);
+ if (outflags & (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP))
+ // TODO: Bug 946423: how do we notify the upper layers about this?
+ close();
+}
+
+void NrSocket::OnSocketDetached(PRFileDesc *fd) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "Socket %p detached", fd);
+}
+
+void NrSocket::IsLocal(bool *aIsLocal) {
+ // TODO(jesup): better check? Does it matter? (likely no)
+ *aIsLocal = false;
+}
+
+// async_event APIs
+int NrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line) {
+ int r = NrSocketBase::async_wait(how, cb, cb_arg, function, line);
+
+ if (!r) {
+ mPollFlags = poll_flags();
+ }
+
+ return r;
+}
+
+int NrSocket::cancel(int how) {
+ int r = NrSocketBase::cancel(how);
+
+ if (!r) {
+ mPollFlags = poll_flags();
+ }
+
+ return r;
+}
+
+// Helper functions for addresses
+static int nr_transport_addr_to_praddr(nr_transport_addr *addr,
+ PRNetAddr *naddr)
+ {
+ int _status;
+
+ memset(naddr, 0, sizeof(*naddr));
+
+ switch(addr->protocol){
+ case IPPROTO_TCP:
+ break;
+ case IPPROTO_UDP:
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ switch(addr->ip_version){
+ case NR_IPV4:
+ naddr->inet.family = PR_AF_INET;
+ naddr->inet.port = addr->u.addr4.sin_port;
+ naddr->inet.ip = addr->u.addr4.sin_addr.s_addr;
+ break;
+ case NR_IPV6:
+ naddr->ipv6.family = PR_AF_INET6;
+ naddr->ipv6.port = addr->u.addr6.sin6_port;
+ naddr->ipv6.flowinfo = addr->u.addr6.sin6_flowinfo;
+ memcpy(&naddr->ipv6.ip, &addr->u.addr6.sin6_addr, sizeof(in6_addr));
+ naddr->ipv6.scope_id = addr->u.addr6.sin6_scope_id;
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+//XXX schien@mozilla.com: copy from PRNetAddrToNetAddr,
+// should be removed after fix the link error in signaling_unittests
+static int praddr_to_netaddr(const PRNetAddr *prAddr, net::NetAddr *addr)
+{
+ int _status;
+
+ switch (prAddr->raw.family) {
+ case PR_AF_INET:
+ addr->inet.family = AF_INET;
+ addr->inet.port = prAddr->inet.port;
+ addr->inet.ip = prAddr->inet.ip;
+ break;
+ case PR_AF_INET6:
+ addr->inet6.family = AF_INET6;
+ addr->inet6.port = prAddr->ipv6.port;
+ addr->inet6.flowinfo = prAddr->ipv6.flowinfo;
+ memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8));
+ addr->inet6.scope_id = prAddr->ipv6.scope_id;
+ break;
+ default:
+ MOZ_ASSERT(false);
+ ABORT(R_BAD_ARGS);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+static int nr_transport_addr_to_netaddr(nr_transport_addr *addr,
+ net::NetAddr *naddr)
+{
+ int r, _status;
+ PRNetAddr praddr;
+
+ if((r = nr_transport_addr_to_praddr(addr, &praddr))) {
+ ABORT(r);
+ }
+
+ if((r = praddr_to_netaddr(&praddr, naddr))) {
+ ABORT(r);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
+ nr_transport_addr *addr, int protocol)
+ {
+ int _status;
+ int r;
+
+ switch(netaddr->raw.family) {
+ case AF_INET:
+ if ((r = nr_ip4_port_to_transport_addr(ntohl(netaddr->inet.ip),
+ ntohs(netaddr->inet.port),
+ protocol, addr)))
+ ABORT(r);
+ break;
+ case AF_INET6:
+ if ((r = nr_ip6_port_to_transport_addr((in6_addr *)&netaddr->inet6.ip.u8,
+ ntohs(netaddr->inet6.port),
+ protocol, addr)))
+ ABORT(r);
+ break;
+ default:
+ MOZ_ASSERT(false);
+ ABORT(R_BAD_ARGS);
+ }
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
+ nr_transport_addr *addr, int protocol,
+ int keep)
+ {
+ int _status;
+ int r;
+ struct sockaddr_in ip4;
+ struct sockaddr_in6 ip6;
+
+ switch(praddr->raw.family) {
+ case PR_AF_INET:
+ ip4.sin_family = PF_INET;
+ ip4.sin_addr.s_addr = praddr->inet.ip;
+ ip4.sin_port = praddr->inet.port;
+ if ((r = nr_sockaddr_to_transport_addr((sockaddr *)&ip4,
+ protocol, keep,
+ addr)))
+ ABORT(r);
+ break;
+ case PR_AF_INET6:
+ ip6.sin6_family = PF_INET6;
+ ip6.sin6_port = praddr->ipv6.port;
+ ip6.sin6_flowinfo = praddr->ipv6.flowinfo;
+ memcpy(&ip6.sin6_addr, &praddr->ipv6.ip, sizeof(in6_addr));
+ ip6.sin6_scope_id = praddr->ipv6.scope_id;
+ if ((r = nr_sockaddr_to_transport_addr((sockaddr *)&ip6,protocol,keep,addr)))
+ ABORT(r);
+ break;
+ default:
+ MOZ_ASSERT(false);
+ ABORT(R_BAD_ARGS);
+ }
+
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+/*
+ * nr_transport_addr_get_addrstring_and_port
+ * convert nr_transport_addr to IP address string and port number
+ */
+int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
+ nsACString *host, int32_t *port) {
+ int r, _status;
+ char addr_string[64];
+
+ // We cannot directly use |nr_transport_addr.as_string| because it contains
+ // more than ip address, therefore, we need to explicity convert it
+ // from |nr_transport_addr_get_addrstring|.
+ if ((r=nr_transport_addr_get_addrstring(addr, addr_string, sizeof(addr_string)))) {
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_get_port(addr, port))) {
+ ABORT(r);
+ }
+
+ *host = addr_string;
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+// nr_socket APIs (as member functions)
+int NrSocket::create(nr_transport_addr *addr) {
+ int r,_status;
+
+ PRStatus status;
+ PRNetAddr naddr;
+
+ nsresult rv;
+ nsCOMPtr<nsISocketTransportService> stservice =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+ if (!NS_SUCCEEDED(rv)) {
+ ABORT(R_INTERNAL);
+ }
+
+ if((r=nr_transport_addr_to_praddr(addr, &naddr)))
+ ABORT(r);
+
+ switch (addr->protocol) {
+ case IPPROTO_UDP:
+ if (!(fd_ = PR_OpenUDPSocket(naddr.raw.family))) {
+ r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create UDP socket, "
+ "family=%d, err=%d", naddr.raw.family, PR_GetError());
+ ABORT(R_INTERNAL);
+ }
+#ifdef XP_WIN
+ if (!mozilla::IsWin8OrLater()) {
+ // Increase default send and receive buffer sizes on <= Win7 to be able to
+ // receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K size)
+ // stream without losing packets.
+ // Manual testing showed that 100K buffer size was not enough and the
+ // packet loss dis-appeared with 256K buffer size.
+ // See bug 1252769 for future improvements of this.
+ PRSize min_buffer_size = 256 * 1024;
+ PRSocketOptionData opt_rcvbuf;
+ opt_rcvbuf.option = PR_SockOpt_RecvBufferSize;
+ if ((status = PR_GetSocketOption(fd_, &opt_rcvbuf)) == PR_SUCCESS) {
+ if (opt_rcvbuf.value.recv_buffer_size < min_buffer_size) {
+ opt_rcvbuf.value.recv_buffer_size = min_buffer_size;
+ if ((status = PR_SetSocketOption(fd_, &opt_rcvbuf)) != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_CRIT,
+ "Couldn't set socket receive buffer size: %d", status);
+ }
+ } else {
+ r_log(LOG_GENERIC, LOG_INFO,
+ "Socket receive buffer size is already: %d",
+ opt_rcvbuf.value.recv_buffer_size);
+ }
+ } else {
+ r_log(LOG_GENERIC, LOG_CRIT,
+ "Couldn't get socket receive buffer size: %d", status);
+ }
+ PRSocketOptionData opt_sndbuf;
+ opt_sndbuf.option = PR_SockOpt_SendBufferSize;
+ if ((status = PR_GetSocketOption(fd_, &opt_sndbuf)) == PR_SUCCESS) {
+ if (opt_sndbuf.value.recv_buffer_size < min_buffer_size) {
+ opt_sndbuf.value.recv_buffer_size = min_buffer_size;
+ if ((status = PR_SetSocketOption(fd_, &opt_sndbuf)) != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_CRIT,
+ "Couldn't set socket send buffer size: %d", status);
+ }
+ } else {
+ r_log(LOG_GENERIC, LOG_INFO,
+ "Socket send buffer size is already: %d",
+ opt_sndbuf.value.recv_buffer_size);
+ }
+ } else {
+ r_log(LOG_GENERIC, LOG_CRIT,
+ "Couldn't get socket send buffer size: %d", status);
+ }
+ }
+#endif
+ break;
+ case IPPROTO_TCP:
+ if (!(fd_ = PR_OpenTCPSocket(naddr.raw.family))) {
+ r_log(LOG_GENERIC,LOG_CRIT,"Couldn't create TCP socket, "
+ "family=%d, err=%d", naddr.raw.family, PR_GetError());
+ ABORT(R_INTERNAL);
+ }
+ // Set ReuseAddr for TCP sockets to enable having several
+ // sockets bound to same local IP and port
+ PRSocketOptionData opt_reuseaddr;
+ opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
+ opt_reuseaddr.value.reuse_addr = PR_TRUE;
+ status = PR_SetSocketOption(fd_, &opt_reuseaddr);
+ if (status != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_CRIT,
+ "Couldn't set reuse addr socket option: %d", status);
+ ABORT(R_INTERNAL);
+ }
+ // And also set ReusePort for platforms supporting this socket option
+ PRSocketOptionData opt_reuseport;
+ opt_reuseport.option = PR_SockOpt_Reuseport;
+ opt_reuseport.value.reuse_port = PR_TRUE;
+ status = PR_SetSocketOption(fd_, &opt_reuseport);
+ if (status != PR_SUCCESS) {
+ if (PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
+ r_log(LOG_GENERIC, LOG_CRIT,
+ "Couldn't set reuse port socket option: %d", status);
+ ABORT(R_INTERNAL);
+ }
+ }
+ // Try to speedup packet delivery by disabling TCP Nagle
+ PRSocketOptionData opt_nodelay;
+ opt_nodelay.option = PR_SockOpt_NoDelay;
+ opt_nodelay.value.no_delay = PR_TRUE;
+ status = PR_SetSocketOption(fd_, &opt_nodelay);
+ if (status != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_WARNING,
+ "Couldn't set Nodelay socket option: %d", status);
+ }
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ status = PR_Bind(fd_, &naddr);
+ if (status != PR_SUCCESS) {
+ r_log(LOG_GENERIC,LOG_CRIT,"Couldn't bind socket to address %s",
+ addr->as_string);
+ ABORT(R_INTERNAL);
+ }
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"Creating socket %p with addr %s",
+ fd_, addr->as_string);
+ nr_transport_addr_copy(&my_addr_,addr);
+
+ /* If we have a wildcard port, patch up the addr */
+ if(nr_transport_addr_is_wildcard(addr)){
+ status = PR_GetSockName(fd_, &naddr);
+ if (status != PR_SUCCESS){
+ r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
+ ABORT(R_INTERNAL);
+ }
+
+ if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,addr->protocol,1)))
+ ABORT(r);
+ }
+
+
+ // Set nonblocking
+ PRSocketOptionData opt_nonblock;
+ opt_nonblock.option = PR_SockOpt_Nonblocking;
+ opt_nonblock.value.non_blocking = PR_TRUE;
+ status = PR_SetSocketOption(fd_, &opt_nonblock);
+ if (status != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_CRIT, "Couldn't make socket nonblocking");
+ ABORT(R_INTERNAL);
+ }
+
+ // Remember our thread.
+ ststhread_ = do_QueryInterface(stservice, &rv);
+ if (!NS_SUCCEEDED(rv))
+ ABORT(R_INTERNAL);
+
+ // Finally, register with the STS
+ rv = stservice->AttachSocket(fd_, this);
+ if (!NS_SUCCEEDED(rv)) {
+ r_log(LOG_GENERIC, LOG_CRIT, "Couldn't attach socket to STS, rv=%u",
+ static_cast<unsigned>(rv));
+ ABORT(R_INTERNAL);
+ }
+
+ _status = 0;
+
+abort:
+ return(_status);
+}
+
+static int ShouldDrop(size_t len) {
+ // Global rate limiting for stun requests, to mitigate the ice hammer DoS
+ // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc)
+
+ // Tolerate rate of 8k/sec, for one second.
+ static SimpleTokenBucket burst(16384*1, 16384);
+ // Tolerate rate of 7.2k/sec over twenty seconds.
+ static SimpleTokenBucket sustained(7372*20, 7372);
+
+ // Check number of tokens in each bucket.
+ if (burst.getTokens(UINT32_MAX) < len) {
+ r_log(LOG_GENERIC, LOG_ERR,
+ "Short term global rate limit for STUN requests exceeded.");
+#ifdef MOZILLA_INTERNAL_API
+ nr_socket_short_term_violation_time = TimeStamp::Now();
+#endif
+
+// Bug 1013007
+#if !EARLY_BETA_OR_EARLIER
+ return R_WOULDBLOCK;
+#else
+ MOZ_ASSERT(false,
+ "Short term global rate limit for STUN requests exceeded. Go "
+ "bug bcampen@mozilla.com if you weren't intentionally "
+ "spamming ICE candidates, or don't know what that means.");
+#endif
+ }
+
+ if (sustained.getTokens(UINT32_MAX) < len) {
+ r_log(LOG_GENERIC, LOG_ERR,
+ "Long term global rate limit for STUN requests exceeded.");
+#ifdef MOZILLA_INTERNAL_API
+ nr_socket_long_term_violation_time = TimeStamp::Now();
+#endif
+// Bug 1013007
+#if !EARLY_BETA_OR_EARLIER
+ return R_WOULDBLOCK;
+#else
+ MOZ_ASSERT(false,
+ "Long term global rate limit for STUN requests exceeded. Go "
+ "bug bcampen@mozilla.com if you weren't intentionally "
+ "spamming ICE candidates, or don't know what that means.");
+#endif
+ }
+
+ // Take len tokens from both buckets.
+ // (not threadsafe, but no problem since this is only called from STS)
+ burst.getTokens(len);
+ sustained.getTokens(len);
+ return 0;
+}
+
+// This should be called on the STS thread.
+int NrSocket::sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) {
+ ASSERT_ON_THREAD(ststhread_);
+ int r,_status;
+ PRNetAddr naddr;
+ int32_t status;
+
+ if ((r=nr_transport_addr_to_praddr(to, &naddr)))
+ ABORT(r);
+
+ if(fd_==nullptr)
+ ABORT(R_EOD);
+
+ if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
+ ABORT(R_WOULDBLOCK);
+ }
+
+ // TODO: Convert flags?
+ status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT);
+ if (status < 0 || (size_t)status != len) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ ABORT(R_WOULDBLOCK);
+
+ r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s: %d",
+ to->as_string, PR_GetError());
+ ABORT(R_IO_ERROR);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int NrSocket::recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) {
+ ASSERT_ON_THREAD(ststhread_);
+ int r,_status;
+ PRNetAddr nfrom;
+ int32_t status;
+
+ status = PR_RecvFrom(fd_, buf, maxlen, flags, &nfrom, PR_INTERVAL_NO_WAIT);
+ if (status <= 0) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ ABORT(R_WOULDBLOCK);
+ r_log(LOG_GENERIC, LOG_INFO, "Error in recvfrom: %d", (int)PR_GetError());
+ ABORT(R_IO_ERROR);
+ }
+ *len = status;
+
+ if((r=nr_praddr_to_transport_addr(&nfrom,from,my_addr_.protocol,0)))
+ ABORT(r);
+
+ //r_log(LOG_GENERIC,LOG_DEBUG,"Read %d bytes from %s",*len,addr->as_string);
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int NrSocket::getaddr(nr_transport_addr *addrp) {
+ ASSERT_ON_THREAD(ststhread_);
+ return nr_transport_addr_copy(addrp, &my_addr_);
+}
+
+// Close the socket so that the STS will detach and then kill it
+void NrSocket::close() {
+ ASSERT_ON_THREAD(ststhread_);
+ mCondition = NS_BASE_STREAM_CLOSED;
+}
+
+
+int NrSocket::connect(nr_transport_addr *addr) {
+ ASSERT_ON_THREAD(ststhread_);
+ int r,_status;
+ PRNetAddr naddr;
+ int32_t connect_status, getsockname_status;
+
+ if ((r=nr_transport_addr_to_praddr(addr, &naddr)))
+ ABORT(r);
+
+ if(!fd_)
+ ABORT(R_EOD);
+
+ // Note: this just means we tried to connect, not that we
+ // are actually live.
+ connect_invoked_ = true;
+ connect_status = PR_Connect(fd_, &naddr, PR_INTERVAL_NO_WAIT);
+ if (connect_status != PR_SUCCESS) {
+ if (PR_GetError() != PR_IN_PROGRESS_ERROR)
+ ABORT(R_IO_ERROR);
+ }
+
+ // If our local address is wildcard, then fill in the
+ // address now.
+ if(nr_transport_addr_is_wildcard(&my_addr_)){
+ getsockname_status = PR_GetSockName(fd_, &naddr);
+ if (getsockname_status != PR_SUCCESS){
+ r_log(LOG_GENERIC, LOG_CRIT, "Couldn't get sock name for socket");
+ ABORT(R_INTERNAL);
+ }
+
+ if((r=nr_praddr_to_transport_addr(&naddr,&my_addr_,addr->protocol,1)))
+ ABORT(r);
+ }
+
+ // Now return the WOULDBLOCK if needed.
+ if (connect_status != PR_SUCCESS) {
+ ABORT(R_WOULDBLOCK);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+
+int NrSocket::write(const void *msg, size_t len, size_t *written) {
+ ASSERT_ON_THREAD(ststhread_);
+ int _status;
+ int32_t status;
+
+ if (!connect_invoked_)
+ ABORT(R_FAILED);
+
+ status = PR_Write(fd_, msg, len);
+ if (status < 0) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ ABORT(R_WOULDBLOCK);
+ r_log(LOG_GENERIC, LOG_INFO, "Error in write");
+ ABORT(R_IO_ERROR);
+ }
+
+ *written = status;
+
+ _status = 0;
+abort:
+ return _status;
+}
+
+int NrSocket::read(void* buf, size_t maxlen, size_t *len) {
+ ASSERT_ON_THREAD(ststhread_);
+ int _status;
+ int32_t status;
+
+ if (!connect_invoked_)
+ ABORT(R_FAILED);
+
+ status = PR_Read(fd_, buf, maxlen);
+ if (status < 0) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ ABORT(R_WOULDBLOCK);
+ r_log(LOG_GENERIC, LOG_INFO, "Error in read");
+ ABORT(R_IO_ERROR);
+ }
+ if (status == 0)
+ ABORT(R_EOD);
+
+ *len = (size_t)status; // Guaranteed to be > 0
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int NrSocket::listen(int backlog) {
+ ASSERT_ON_THREAD(ststhread_);
+ int32_t status;
+ int _status;
+
+ assert(fd_);
+ status = PR_Listen(fd_, backlog);
+ if (status != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_CRIT, "%s: PR_GetError() == %d",
+ __FUNCTION__, PR_GetError());
+ ABORT(R_IO_ERROR);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int NrSocket::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+ ASSERT_ON_THREAD(ststhread_);
+ int _status, r;
+ PRStatus status;
+ PRFileDesc *prfd;
+ PRNetAddr nfrom;
+ NrSocket *sock=nullptr;
+ nsresult rv;
+ PRSocketOptionData opt_nonblock, opt_nodelay;
+ nsCOMPtr<nsISocketTransportService> stservice =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+ if (NS_FAILED(rv)) {
+ ABORT(R_INTERNAL);
+ }
+
+ if(!fd_)
+ ABORT(R_EOD);
+
+ prfd = PR_Accept(fd_, &nfrom, PR_INTERVAL_NO_WAIT);
+
+ if (!prfd) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ ABORT(R_WOULDBLOCK);
+
+ ABORT(R_IO_ERROR);
+ }
+
+ sock = new NrSocket();
+
+ sock->fd_=prfd;
+ nr_transport_addr_copy(&sock->my_addr_, &my_addr_);
+
+ if((r=nr_praddr_to_transport_addr(&nfrom, addrp, my_addr_.protocol, 0)))
+ ABORT(r);
+
+ // Set nonblocking
+ opt_nonblock.option = PR_SockOpt_Nonblocking;
+ opt_nonblock.value.non_blocking = PR_TRUE;
+ status = PR_SetSocketOption(prfd, &opt_nonblock);
+ if (status != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_CRIT,
+ "Failed to make accepted socket nonblocking: %d", status);
+ ABORT(R_INTERNAL);
+ }
+ // Disable TCP Nagle
+ opt_nodelay.option = PR_SockOpt_NoDelay;
+ opt_nodelay.value.no_delay = PR_TRUE;
+ status = PR_SetSocketOption(prfd, &opt_nodelay);
+ if (status != PR_SUCCESS) {
+ r_log(LOG_GENERIC, LOG_WARNING,
+ "Failed to set Nodelay on accepted socket: %d", status);
+ }
+
+ // Should fail only with OOM
+ if ((r=nr_socket_create_int(static_cast<void *>(sock), sock->vtbl(), sockp)))
+ ABORT(r);
+
+ // Remember our thread.
+ sock->ststhread_ = do_QueryInterface(stservice, &rv);
+ if (NS_FAILED(rv))
+ ABORT(R_INTERNAL);
+
+ // Finally, register with the STS
+ rv = stservice->AttachSocket(prfd, sock);
+ if (NS_FAILED(rv)) {
+ ABORT(R_INTERNAL);
+ }
+
+ sock->connect_invoked_ = true;
+
+ // Add a reference so that we can delete it in destroy()
+ sock->AddRef();
+ _status = 0;
+abort:
+ if (_status) {
+ delete sock;
+ }
+
+ return(_status);
+}
+
+NS_IMPL_ISUPPORTS(NrUdpSocketIpcProxy, nsIUDPSocketInternal)
+
+nsresult
+NrUdpSocketIpcProxy::Init(const RefPtr<NrUdpSocketIpc>& socket)
+{
+ nsresult rv;
+ sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "Failed to get STS thread");
+ return rv;
+ }
+
+ socket_ = socket;
+ return NS_OK;
+}
+
+NrUdpSocketIpcProxy::~NrUdpSocketIpcProxy()
+{
+ // Send our ref to STS to be released
+ RUN_ON_THREAD(sts_thread_,
+ mozilla::WrapRelease(socket_.forget()),
+ NS_DISPATCH_NORMAL);
+}
+
+// IUDPSocketInternal interfaces
+// callback while error happened in UDP socket operation
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerError(const nsACString &message,
+ const nsACString &filename,
+ uint32_t line_number) {
+ return socket_->CallListenerError(message, filename, line_number);
+}
+
+// callback while receiving UDP packet
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerReceivedData(const nsACString &host,
+ uint16_t port,
+ const uint8_t *data,
+ uint32_t data_length) {
+ return socket_->CallListenerReceivedData(host, port, data, data_length);
+}
+
+// callback while UDP socket is opened
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerOpened() {
+ return socket_->CallListenerOpened();
+}
+
+// callback while UDP socket is connected
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerConnected() {
+ return socket_->CallListenerConnected();
+}
+
+// callback while UDP socket is closed
+NS_IMETHODIMP NrUdpSocketIpcProxy::CallListenerClosed() {
+ return socket_->CallListenerClosed();
+}
+
+// NrUdpSocketIpc Implementation
+NrUdpSocketIpc::NrUdpSocketIpc()
+ : NrSocketIpc(GetIOThreadAndAddUse_s()),
+ monitor_("NrUdpSocketIpc"),
+ err_(false),
+ state_(NR_INIT) {
+}
+
+NrUdpSocketIpc::~NrUdpSocketIpc()
+{
+ // also guarantees socket_child_ is released from the io_thread, and
+ // tells the SingletonThreadHolder we're done with it
+
+#if defined(MOZILLA_INTERNAL_API)
+ // close(), but transfer the socket_child_ reference to die as well
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnableNM(&NrUdpSocketIpc::release_child_i,
+ socket_child_.forget().take(),
+ sts_thread_),
+ NS_DISPATCH_NORMAL);
+#endif
+}
+
+// IUDPSocketInternal interfaces
+// callback while error happened in UDP socket operation
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerError(const nsACString &message,
+ const nsACString &filename,
+ uint32_t line_number) {
+ ASSERT_ON_THREAD(io_thread_);
+
+ r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d this=%p",
+ message.BeginReading(), filename.BeginReading(), line_number, (void*) this );
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+ err_ = true;
+ monitor_.NotifyAll();
+
+ return NS_OK;
+}
+
+// callback while receiving UDP packet
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerReceivedData(const nsACString &host,
+ uint16_t port,
+ const uint8_t *data,
+ uint32_t data_length) {
+ ASSERT_ON_THREAD(io_thread_);
+
+ PRNetAddr addr;
+ memset(&addr, 0, sizeof(addr));
+
+ {
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ if (PR_SUCCESS != PR_StringToNetAddr(host.BeginReading(), &addr)) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to convert remote host to PRNetAddr");
+ return NS_OK;
+ }
+
+ // Use PR_IpAddrNull to avoid address being reset to 0.
+ if (PR_SUCCESS != PR_SetNetAddr(PR_IpAddrNull, addr.raw.family, port, &addr)) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
+ return NS_OK;
+ }
+ }
+
+ nsAutoPtr<DataBuffer> buf(new DataBuffer(data, data_length));
+ RefPtr<nr_udp_message> msg(new nr_udp_message(addr, buf));
+
+ RUN_ON_THREAD(sts_thread_,
+ mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
+ &NrUdpSocketIpc::recv_callback_s,
+ msg),
+ NS_DISPATCH_NORMAL);
+ return NS_OK;
+}
+
+nsresult NrUdpSocketIpc::SetAddress() {
+ uint16_t port;
+ if (NS_FAILED(socket_child_->GetLocalPort(&port))) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to get local port");
+ return NS_OK;
+ }
+
+ nsAutoCString address;
+ if(NS_FAILED(socket_child_->GetLocalAddress(address))) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to get local address");
+ return NS_OK;
+ }
+
+ PRNetAddr praddr;
+ if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, port, &praddr)) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to set port in PRNetAddr");
+ return NS_OK;
+ }
+
+ if (PR_SUCCESS != PR_StringToNetAddr(address.BeginReading(), &praddr)) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to convert local host to PRNetAddr");
+ return NS_OK;
+ }
+
+ nr_transport_addr expected_addr;
+ if(nr_transport_addr_copy(&expected_addr, &my_addr_)) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to copy my_addr_");
+ }
+
+ if (nr_praddr_to_transport_addr(&praddr, &my_addr_, IPPROTO_UDP, 1)) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to copy local host to my_addr_");
+ }
+
+ if (!nr_transport_addr_is_wildcard(&expected_addr) &&
+ nr_transport_addr_cmp(&expected_addr, &my_addr_,
+ NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
+ err_ = true;
+ MOZ_ASSERT(false, "Address of opened socket is not expected");
+ }
+
+ return NS_OK;
+}
+
+// callback while UDP socket is opened
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerOpened() {
+ ASSERT_ON_THREAD(io_thread_);
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket opened this=%p", (void*) this);
+ nsresult rv = SetAddress();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mon.NotifyAll();
+
+ return NS_OK;
+}
+
+// callback while UDP socket is connected
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerConnected() {
+ ASSERT_ON_THREAD(io_thread_);
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket connected this=%p", (void*) this);
+ MOZ_ASSERT(state_ == NR_CONNECTED);
+
+ nsresult rv = SetAddress();
+ if (NS_FAILED(rv)) {
+ mon.NotifyAll();
+ return rv;
+ }
+
+ r_log(LOG_GENERIC, LOG_INFO, "Exit UDP socket connected");
+ mon.NotifyAll();
+
+ return NS_OK;
+}
+
+// callback while UDP socket is closed
+NS_IMETHODIMP NrUdpSocketIpc::CallListenerClosed() {
+ ASSERT_ON_THREAD(io_thread_);
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "UDP socket closed this=%p", (void*) this);
+ MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING);
+ state_ = NR_CLOSED;
+
+ return NS_OK;
+}
+
+//
+// NrSocketBase methods.
+//
+int NrUdpSocketIpc::create(nr_transport_addr *addr) {
+ ASSERT_ON_THREAD(sts_thread_);
+
+ int r, _status;
+ nsresult rv;
+ int32_t port;
+ nsCString host;
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ if (state_ != NR_INIT) {
+ ABORT(R_INTERNAL);
+ }
+
+ sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "Failed to get STS thread");
+ ABORT(R_INTERNAL);
+ }
+
+ if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
+ ABORT(r);
+ }
+
+ // wildcard address will be resolved at NrUdpSocketIpc::CallListenerVoid
+ if ((r=nr_transport_addr_copy(&my_addr_, addr))) {
+ ABORT(r);
+ }
+
+ state_ = NR_CONNECTING;
+
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
+ &NrUdpSocketIpc::create_i,
+ host, static_cast<uint16_t>(port)),
+ NS_DISPATCH_NORMAL);
+
+ // Wait until socket creation complete.
+ mon.Wait();
+
+ if (err_) {
+ close();
+ ABORT(R_INTERNAL);
+ }
+
+ state_ = NR_CONNECTED;
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int NrUdpSocketIpc::sendto(const void *msg, size_t len, int flags,
+ nr_transport_addr *to) {
+ ASSERT_ON_THREAD(sts_thread_);
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ //If send err happened before, simply return the error.
+ if (err_) {
+ return R_IO_ERROR;
+ }
+
+ if (state_ != NR_CONNECTED) {
+ return R_INTERNAL;
+ }
+
+ int r;
+ net::NetAddr addr;
+ if ((r=nr_transport_addr_to_netaddr(to, &addr))) {
+ return r;
+ }
+
+ if (nr_is_stun_request_message((UCHAR*)msg, len) && ShouldDrop(len)) {
+ return R_WOULDBLOCK;
+ }
+
+ nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t*>(msg), len));
+
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
+ &NrUdpSocketIpc::sendto_i,
+ addr, buf),
+ NS_DISPATCH_NORMAL);
+ return 0;
+}
+
+void NrUdpSocketIpc::close() {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::close()");
+
+ ASSERT_ON_THREAD(sts_thread_);
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+ state_ = NR_CLOSING;
+
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
+ &NrUdpSocketIpc::close_i),
+ NS_DISPATCH_NORMAL);
+
+ //remove all enqueued messages
+ std::queue<RefPtr<nr_udp_message> > empty;
+ std::swap(received_msgs_, empty);
+}
+
+int NrUdpSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
+ nr_transport_addr *from) {
+ ASSERT_ON_THREAD(sts_thread_);
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ int r, _status;
+ uint32_t consumed_len;
+
+ *len = 0;
+
+ if (state_ != NR_CONNECTED) {
+ ABORT(R_INTERNAL);
+ }
+
+ if (received_msgs_.empty()) {
+ ABORT(R_WOULDBLOCK);
+ }
+
+ {
+ RefPtr<nr_udp_message> msg(received_msgs_.front());
+
+ received_msgs_.pop();
+
+ if ((r=nr_praddr_to_transport_addr(&msg->from, from, IPPROTO_UDP, 0))) {
+ err_ = true;
+ MOZ_ASSERT(false, "Get bogus address for received UDP packet");
+ ABORT(r);
+ }
+
+ consumed_len = std::min(maxlen, msg->data->len());
+ if (consumed_len < msg->data->len()) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "Partial received UDP packet will be discard");
+ }
+
+ memcpy(buf, msg->data->data(), consumed_len);
+ *len = consumed_len;
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int NrUdpSocketIpc::getaddr(nr_transport_addr *addrp) {
+ ASSERT_ON_THREAD(sts_thread_);
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ if (state_ != NR_CONNECTED) {
+ return R_INTERNAL;
+ }
+
+ return nr_transport_addr_copy(addrp, &my_addr_);
+}
+
+int NrUdpSocketIpc::connect(nr_transport_addr *addr) {
+ int r,_status;
+ int32_t port;
+ nsCString host;
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::connect(%s) this=%p", addr->as_string,
+ (void*) this);
+
+ if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
+ ABORT(r);
+ }
+
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
+ &NrUdpSocketIpc::connect_i,
+ host, static_cast<uint16_t>(port)),
+ NS_DISPATCH_NORMAL);
+
+ // Wait until connect() completes.
+ mon.Wait();
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrUdpSocketIpc::connect this=%p completed err_ = %s",
+ (void*) this, err_ ? "true" : "false");
+
+ if (err_) {
+ ABORT(R_INTERNAL);
+ }
+
+ _status = 0;
+abort:
+ return _status;
+}
+
+int NrUdpSocketIpc::write(const void *msg, size_t len, size_t *written) {
+ MOZ_ASSERT(false);
+ return R_INTERNAL;
+}
+
+int NrUdpSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+ MOZ_ASSERT(false);
+ return R_INTERNAL;
+}
+
+int NrUdpSocketIpc::listen(int backlog) {
+ MOZ_ASSERT(false);
+ return R_INTERNAL;
+}
+
+int NrUdpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+ MOZ_ASSERT(false);
+ return R_INTERNAL;
+}
+
+// IO thread executors
+void NrUdpSocketIpc::create_i(const nsACString &host, const uint16_t port) {
+ ASSERT_ON_THREAD(io_thread_);
+
+ uint32_t minBuffSize = 0;
+ nsresult rv;
+ nsCOMPtr<nsIUDPSocketChild> socketChild = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
+ if (NS_FAILED(rv)) {
+ ReentrantMonitorAutoEnter mon(monitor_);
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to create UDPSocketChild");
+ return;
+ }
+
+ // This can spin the event loop; don't do that with the monitor held
+ socketChild->SetBackgroundSpinsEvents();
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+ if (!socket_child_) {
+ socket_child_ = socketChild;
+ socket_child_->SetFilterName(nsCString(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX));
+ } else {
+ socketChild = nullptr;
+ }
+
+ RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
+ rv = proxy->Init(this);
+ if (NS_FAILED(rv)) {
+ err_ = true;
+ mon.NotifyAll();
+ return;
+ }
+
+#ifdef XP_WIN
+ if (!mozilla::IsWin8OrLater()) {
+ // Increase default receive and send buffer size on <= Win7 to be able to
+ // receive and send an unpaced HD (>= 720p = 1280x720 - I Frame ~ 21K size)
+ // stream without losing packets.
+ // Manual testing showed that 100K buffer size was not enough and the
+ // packet loss dis-appeared with 256K buffer size.
+ // See bug 1252769 for future improvements of this.
+ minBuffSize = 256 * 1024;
+ }
+#endif
+ // XXX bug 1126232 - don't use null Principal!
+ if (NS_FAILED(socket_child_->Bind(proxy, nullptr, host, port,
+ /* reuse = */ false,
+ /* loopback = */ false,
+ /* recv buffer size */ minBuffSize,
+ /* send buffer size */ minBuffSize))) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to create UDP socket");
+ mon.NotifyAll();
+ return;
+ }
+}
+
+void NrUdpSocketIpc::connect_i(const nsACString &host, const uint16_t port) {
+ ASSERT_ON_THREAD(io_thread_);
+ nsresult rv;
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
+ rv = proxy->Init(this);
+ if (NS_FAILED(rv)) {
+ err_ = true;
+ mon.NotifyAll();
+ return;
+ }
+
+ if (NS_FAILED(socket_child_->Connect(proxy, host, port))) {
+ err_ = true;
+ MOZ_ASSERT(false, "Failed to connect UDP socket");
+ mon.NotifyAll();
+ return;
+ }
+}
+
+
+void NrUdpSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
+ ASSERT_ON_THREAD(io_thread_);
+
+ ReentrantMonitorAutoEnter mon(monitor_);
+
+ if (!socket_child_) {
+ MOZ_ASSERT(false);
+ err_ = true;
+ return;
+ }
+ if (NS_FAILED(socket_child_->SendWithAddress(&addr,
+ buf->data(),
+ buf->len()))) {
+ err_ = true;
+ }
+}
+
+void NrUdpSocketIpc::close_i() {
+ ASSERT_ON_THREAD(io_thread_);
+
+ if (socket_child_) {
+ socket_child_->Close();
+ socket_child_ = nullptr;
+ }
+}
+
+#if defined(MOZILLA_INTERNAL_API)
+// close(), but transfer the socket_child_ reference to die as well
+// static
+void NrUdpSocketIpc::release_child_i(nsIUDPSocketChild* aChild,
+ nsCOMPtr<nsIEventTarget> sts_thread) {
+ RefPtr<nsIUDPSocketChild> socket_child_ref =
+ already_AddRefed<nsIUDPSocketChild>(aChild);
+ if (socket_child_ref) {
+ socket_child_ref->Close();
+ }
+ // Tell SingletonThreadHolder we're done with it
+ RUN_ON_THREAD(sts_thread,
+ mozilla::WrapRunnableNM(&NrUdpSocketIpc::release_use_s),
+ NS_DISPATCH_NORMAL);
+}
+
+void NrUdpSocketIpc::release_use_s() {
+ sThread->ReleaseUse();
+}
+#endif
+
+void NrUdpSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
+ ASSERT_ON_THREAD(sts_thread_);
+
+ {
+ ReentrantMonitorAutoEnter mon(monitor_);
+ if (state_ != NR_CONNECTED) {
+ return;
+ }
+ }
+
+ //enqueue received message
+ received_msgs_.push(msg);
+
+ if ((poll_flags() & PR_POLL_READ)) {
+ fire_callback(NR_ASYNC_WAIT_READ);
+ }
+}
+
+#if defined(MOZILLA_INTERNAL_API)
+// TCPSocket.
+class NrTcpSocketIpc::TcpSocketReadyRunner: public Runnable
+{
+public:
+ explicit TcpSocketReadyRunner(NrTcpSocketIpc *sck)
+ : socket_(sck) {}
+
+ NS_IMETHOD Run() override {
+ socket_->maybe_post_socket_ready();
+ return NS_OK;
+ }
+
+private:
+ RefPtr<NrTcpSocketIpc> socket_;
+};
+
+
+NS_IMPL_ISUPPORTS(NrTcpSocketIpc,
+ nsITCPSocketCallback)
+
+NrTcpSocketIpc::NrTcpSocketIpc(nsIThread* aThread)
+ : NrSocketIpc(static_cast<nsIEventTarget*>(aThread)),
+ mirror_state_(NR_INIT),
+ state_(NR_INIT),
+ buffered_bytes_(0),
+ tracking_number_(0) {
+}
+
+NrTcpSocketIpc::~NrTcpSocketIpc()
+{
+ // also guarantees socket_child_ is released from the io_thread
+
+ // close(), but transfer the socket_child_ reference to die as well
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnableNM(&NrTcpSocketIpc::release_child_i,
+ socket_child_.forget().take(),
+ sts_thread_),
+ NS_DISPATCH_NORMAL);
+}
+
+//
+// nsITCPSocketCallback methods
+//
+NS_IMETHODIMP NrTcpSocketIpc::UpdateReadyState(uint32_t aReadyState) {
+ NrSocketIpcState temp = NR_INIT;
+ switch (static_cast<dom::TCPReadyState>(aReadyState)) {
+ case dom::TCPReadyState::Connecting:
+ temp = NR_CONNECTING;
+ break;
+ case dom::TCPReadyState::Open:
+ temp = NR_CONNECTED;
+ break;
+ case dom::TCPReadyState::Closing:
+ temp = NR_CLOSING;
+ break;
+ case dom::TCPReadyState::Closed:
+ temp = NR_CLOSED;
+ break;
+ default:
+ MOZ_ASSERT(false, "Invalid ReadyState");
+ return NS_OK;
+ }
+ if (mirror_state_ != temp) {
+ mirror_state_ = temp;
+ RUN_ON_THREAD(sts_thread_,
+ mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
+ &NrTcpSocketIpc::update_state_s,
+ temp),
+ NS_DISPATCH_NORMAL);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::UpdateBufferedAmount(uint32_t buffered_amount,
+ uint32_t tracking_number) {
+ RUN_ON_THREAD(sts_thread_,
+ mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
+ &NrTcpSocketIpc::message_sent_s,
+ buffered_amount,
+ tracking_number),
+ NS_DISPATCH_NORMAL);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataArrayEvent(const nsAString& aType,
+ const InfallibleTArray<uint8_t>& buffer) {
+ // Called when we received data.
+ uint8_t *buf = const_cast<uint8_t*>(buffer.Elements());
+
+ nsAutoPtr<DataBuffer> data_buf(new DataBuffer(buf, buffer.Length()));
+ RefPtr<nr_tcp_message> msg = new nr_tcp_message(data_buf);
+
+ RUN_ON_THREAD(sts_thread_,
+ mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
+ &NrTcpSocketIpc::recv_message_s,
+ msg),
+ NS_DISPATCH_NORMAL);
+ return NS_OK;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireErrorEvent(const nsAString &type,
+ const nsAString &name) {
+ r_log(LOG_GENERIC, LOG_ERR,
+ "Error from TCPSocketChild: type: %s, name: %s",
+ NS_LossyConvertUTF16toASCII(type).get(), NS_LossyConvertUTF16toASCII(name).get());
+ socket_child_ = nullptr;
+
+ mirror_state_ = NR_CLOSED;
+ RUN_ON_THREAD(sts_thread_,
+ mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
+ &NrTcpSocketIpc::update_state_s,
+ NR_CLOSED),
+ NS_DISPATCH_NORMAL);
+
+ return NS_OK;
+}
+
+// methods of nsITCPSocketCallback that we are not going to implement.
+
+NS_IMETHODIMP NrTcpSocketIpc::FireDataStringEvent(const nsAString &type,
+ const nsACString &data) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP NrTcpSocketIpc::FireEvent(const nsAString &type) {
+ // XXX support type.mData == 'close' at least
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//
+// NrSocketBase methods.
+//
+int NrTcpSocketIpc::create(nr_transport_addr *addr) {
+ int r, _status;
+ nsresult rv;
+ int32_t port;
+ nsCString host;
+
+ if (state_ != NR_INIT) {
+ ABORT(R_INTERNAL);
+ }
+
+ sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "Failed to get STS thread");
+ ABORT(R_INTERNAL);
+ }
+
+ // Sanity check
+ if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) {
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_copy(&my_addr_, addr))) {
+ ABORT(r);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int NrTcpSocketIpc::sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) {
+ MOZ_ASSERT(false);
+ return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) {
+ MOZ_ASSERT(false);
+ return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::getaddr(nr_transport_addr *addrp) {
+ ASSERT_ON_THREAD(sts_thread_);
+ return nr_transport_addr_copy(addrp, &my_addr_);
+}
+
+void NrTcpSocketIpc::close() {
+ ASSERT_ON_THREAD(sts_thread_);
+
+ if (state_ == NR_CLOSED || state_ == NR_CLOSING) {
+ return;
+ }
+
+ state_ = NR_CLOSING;
+
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
+ &NrTcpSocketIpc::close_i),
+ NS_DISPATCH_NORMAL);
+
+ //remove all enqueued messages
+ std::queue<RefPtr<nr_tcp_message>> empty;
+ std::swap(msg_queue_, empty);
+}
+
+int NrTcpSocketIpc::connect(nr_transport_addr *addr) {
+ nsCString remote_addr, local_addr;
+ int32_t remote_port, local_port;
+ int r, _status;
+ if ((r=nr_transport_addr_get_addrstring_and_port(addr,
+ &remote_addr,
+ &remote_port))) {
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_get_addrstring_and_port(&my_addr_,
+ &local_addr,
+ &local_port))) {
+ MOZ_ASSERT(false); // shouldn't fail as it was sanity-checked in ::create()
+ ABORT(r);
+ }
+
+ state_ = mirror_state_ = NR_CONNECTING;
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
+ &NrTcpSocketIpc::connect_i,
+ remote_addr,
+ static_cast<uint16_t>(remote_port),
+ local_addr,
+ static_cast<uint16_t>(local_port)),
+ NS_DISPATCH_NORMAL);
+
+ // Make caller wait for ready to write.
+ _status = R_WOULDBLOCK;
+ abort:
+ return _status;
+}
+
+int NrTcpSocketIpc::write(const void *msg, size_t len, size_t *written) {
+ ASSERT_ON_THREAD(sts_thread_);
+ int _status = 0;
+ if (state_ != NR_CONNECTED) {
+ ABORT(R_FAILED);
+ }
+
+ if (buffered_bytes_ + len >= nsITCPSocketCallback::BUFFER_SIZE) {
+ ABORT(R_WOULDBLOCK);
+ }
+
+ buffered_bytes_ += len;
+ {
+ InfallibleTArray<uint8_t>* arr = new InfallibleTArray<uint8_t>();
+ arr->AppendElements(static_cast<const uint8_t*>(msg), len);
+ // keep track of un-acknowleged writes by tracking number.
+ writes_in_flight_.push_back(len);
+ RUN_ON_THREAD(io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this),
+ &NrTcpSocketIpc::write_i,
+ nsAutoPtr<InfallibleTArray<uint8_t>>(arr),
+ ++tracking_number_),
+ NS_DISPATCH_NORMAL);
+ }
+ *written = len;
+ abort:
+ return _status;
+}
+
+int NrTcpSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
+ int _status = 0;
+ if (state_ != NR_CONNECTED) {
+ ABORT(R_FAILED);
+ }
+
+ if (msg_queue_.size() == 0) {
+ ABORT(R_WOULDBLOCK);
+ }
+
+ {
+ RefPtr<nr_tcp_message> msg(msg_queue_.front());
+ size_t consumed_len = std::min(maxlen, msg->unread_bytes());
+ memcpy(buf, msg->reading_pointer(), consumed_len);
+ if (consumed_len < msg->unread_bytes()) {
+ // There is still something left in buffer.
+ msg->read_bytes += consumed_len;
+ } else {
+ msg_queue_.pop();
+ }
+ *len = consumed_len;
+ }
+
+ abort:
+ return _status;
+}
+
+int NrTcpSocketIpc::listen(int backlog) {
+ return R_INTERNAL;
+}
+
+int NrTcpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+ return R_INTERNAL;
+}
+
+void NrTcpSocketIpc::connect_i(const nsACString &remote_addr,
+ uint16_t remote_port,
+ const nsACString &local_addr,
+ uint16_t local_port) {
+ ASSERT_ON_THREAD(io_thread_);
+ mirror_state_ = NR_CONNECTING;
+
+ dom::TCPSocketChild* child = new dom::TCPSocketChild(NS_ConvertUTF8toUTF16(remote_addr), remote_port);
+ socket_child_ = child;
+
+ // Bug 1285330: put filtering back in here
+
+ // XXX remove remote!
+ socket_child_->SendWindowlessOpenBind(this,
+ remote_addr, remote_port,
+ local_addr, local_port,
+ /* use ssl */ false);
+}
+
+void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,
+ uint32_t tracking_number) {
+ ASSERT_ON_THREAD(io_thread_);
+ if (!socket_child_) {
+ return;
+ }
+ socket_child_->SendSendArray(*arr, tracking_number);
+}
+
+void NrTcpSocketIpc::close_i() {
+ ASSERT_ON_THREAD(io_thread_);
+ mirror_state_ = NR_CLOSING;
+ if (!socket_child_) {
+ return;
+ }
+ socket_child_->SendClose();
+}
+
+// close(), but transfer the socket_child_ reference to die as well
+// static
+void NrTcpSocketIpc::release_child_i(dom::TCPSocketChild* aChild,
+ nsCOMPtr<nsIEventTarget> sts_thread) {
+ RefPtr<dom::TCPSocketChild> socket_child_ref =
+ already_AddRefed<dom::TCPSocketChild>(aChild);
+ if (socket_child_ref) {
+ socket_child_ref->SendClose();
+ }
+ // io_thread_ is MainThread, so no use to release
+}
+
+void NrTcpSocketIpc::message_sent_s(uint32_t buffered_amount,
+ uint32_t tracking_number) {
+ ASSERT_ON_THREAD(sts_thread_);
+
+ size_t num_unacked_writes = tracking_number_ - tracking_number;
+ while (writes_in_flight_.size() > num_unacked_writes) {
+ writes_in_flight_.pop_front();
+ }
+
+ for (size_t unacked_write_len : writes_in_flight_) {
+ buffered_amount += unacked_write_len;
+ }
+
+ r_log(LOG_GENERIC, LOG_ERR,
+ "UpdateBufferedAmount: (tracking %u): %u, waiting: %s",
+ tracking_number, buffered_amount,
+ (poll_flags() & PR_POLL_WRITE) ? "yes" : "no");
+
+ buffered_bytes_ = buffered_amount;
+ maybe_post_socket_ready();
+}
+
+void NrTcpSocketIpc::recv_message_s(nr_tcp_message *msg) {
+ ASSERT_ON_THREAD(sts_thread_);
+ msg_queue_.push(msg);
+ maybe_post_socket_ready();
+}
+
+void NrTcpSocketIpc::update_state_s(NrSocketIpcState next_state) {
+ ASSERT_ON_THREAD(sts_thread_);
+ // only allow valid transitions
+ switch (state_) {
+ case NR_CONNECTING:
+ if (next_state == NR_CONNECTED) {
+ state_ = NR_CONNECTED;
+ maybe_post_socket_ready();
+ } else {
+ state_ = next_state; // all states are valid from CONNECTING
+ }
+ break;
+ case NR_CONNECTED:
+ if (next_state != NR_CONNECTING) {
+ state_ = next_state;
+ }
+ break;
+ case NR_CLOSING:
+ if (next_state == NR_CLOSED) {
+ state_ = next_state;
+ }
+ break;
+ case NR_CLOSED:
+ break;
+ default:
+ MOZ_CRASH("update_state_s while in illegal state");
+ }
+}
+
+void NrTcpSocketIpc::maybe_post_socket_ready() {
+ bool has_event = false;
+ if (state_ == NR_CONNECTED) {
+ if (poll_flags() & PR_POLL_WRITE) {
+ // This effectively polls via the event loop until the
+ // NR_ASYNC_WAIT_WRITE is no longer armed.
+ if (buffered_bytes_ < nsITCPSocketCallback::BUFFER_SIZE) {
+ r_log(LOG_GENERIC, LOG_INFO, "Firing write callback (%u)",
+ (uint32_t)buffered_bytes_);
+ fire_callback(NR_ASYNC_WAIT_WRITE);
+ has_event = true;
+ }
+ }
+ if (poll_flags() & PR_POLL_READ) {
+ if (msg_queue_.size()) {
+ r_log(LOG_GENERIC, LOG_INFO, "Firing read callback (%u)",
+ (uint32_t)msg_queue_.size());
+ fire_callback(NR_ASYNC_WAIT_READ);
+ has_event = true;
+ }
+ }
+ }
+
+ // If any event has been posted, we post a runnable to see
+ // if the events have to be posted again.
+ if (has_event) {
+ RefPtr<TcpSocketReadyRunner> runnable = new TcpSocketReadyRunner(this);
+ NS_DispatchToCurrentThread(runnable);
+ }
+}
+#endif
+
+} // close namespace
+
+
+using namespace mozilla;
+
+// Bridge to the nr_socket interface
+static int nr_socket_local_destroy(void **objp);
+static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
+ int flags, nr_transport_addr *to);
+static int nr_socket_local_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
+static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_local_close(void *obj);
+static int nr_socket_local_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_local_write(void *obj,const void *msg, size_t len,
+ size_t *written);
+static int nr_socket_local_read(void *obj,void * restrict buf, size_t maxlen,
+ size_t *len);
+static int nr_socket_local_listen(void *obj, int backlog);
+static int nr_socket_local_accept(void *obj, nr_transport_addr *addrp,
+ nr_socket **sockp);
+
+static nr_socket_vtbl nr_socket_local_vtbl={
+ 2,
+ nr_socket_local_destroy,
+ nr_socket_local_sendto,
+ nr_socket_local_recvfrom,
+ nr_socket_local_getfd,
+ nr_socket_local_getaddr,
+ nr_socket_local_connect,
+ nr_socket_local_write,
+ nr_socket_local_read,
+ nr_socket_local_close,
+ nr_socket_local_listen,
+ nr_socket_local_accept
+};
+
+/* static */
+int
+NrSocketBase::CreateSocket(nr_transport_addr *addr, RefPtr<NrSocketBase> *sock)
+{
+ int r, _status;
+
+ // create IPC bridge for content process
+ if (XRE_IsParentProcess()) {
+ *sock = new NrSocket();
+ } else {
+ switch (addr->protocol) {
+ case IPPROTO_UDP:
+ *sock = new NrUdpSocketIpc();
+ break;
+ case IPPROTO_TCP:
+#if defined(MOZILLA_INTERNAL_API)
+ {
+ nsCOMPtr<nsIThread> main_thread;
+ NS_GetMainThread(getter_AddRefs(main_thread));
+ *sock = new NrTcpSocketIpc(main_thread.get());
+ }
+#else
+ ABORT(R_REJECTED);
+#endif
+ break;
+ }
+ }
+
+ r = (*sock)->create(addr);
+ if (r)
+ ABORT(r);
+
+ _status = 0;
+abort:
+ if (_status) {
+ *sock = nullptr;
+ }
+ return _status;
+}
+
+int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp) {
+ RefPtr<NrSocketBase> sock;
+ int r, _status;
+
+ r = NrSocketBase::CreateSocket(addr, &sock);
+ if (r) {
+ ABORT(r);
+ }
+
+ r = nr_socket_create_int(static_cast<void *>(sock),
+ sock->vtbl(), sockp);
+ if (r)
+ ABORT(r);
+
+ _status = 0;
+
+ {
+ // We will release this reference in destroy(), not exactly the normal
+ // ownership model, but it is what it is.
+ NrSocketBase* dummy = sock.forget().take();
+ (void)dummy;
+ }
+
+abort:
+ return _status;
+}
+
+
+static int nr_socket_local_destroy(void **objp) {
+ if(!objp || !*objp)
+ return 0;
+
+ NrSocketBase *sock = static_cast<NrSocketBase *>(*objp);
+ *objp = 0;
+
+ sock->close(); // Signal STS that we want not to listen
+ sock->Release(); // Decrement the ref count
+
+ return 0;
+}
+
+static int nr_socket_local_sendto(void *obj,const void *msg, size_t len,
+ int flags, nr_transport_addr *addr) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->sendto(msg, len, flags, addr);
+}
+
+static int nr_socket_local_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags,
+ nr_transport_addr *addr) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->recvfrom(buf, maxlen, len, flags, addr);
+}
+
+static int nr_socket_local_getfd(void *obj, NR_SOCKET *fd) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ *fd = sock;
+
+ return 0;
+}
+
+static int nr_socket_local_getaddr(void *obj, nr_transport_addr *addrp) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->getaddr(addrp);
+}
+
+
+static int nr_socket_local_close(void *obj) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ sock->close();
+
+ return 0;
+}
+
+static int nr_socket_local_write(void *obj, const void *msg, size_t len,
+ size_t *written) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->write(msg, len, written);
+}
+
+static int nr_socket_local_read(void *obj, void * restrict buf, size_t maxlen,
+ size_t *len) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->read(buf, maxlen, len);
+}
+
+static int nr_socket_local_connect(void *obj, nr_transport_addr *addr) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->connect(addr);
+}
+
+static int nr_socket_local_listen(void *obj, int backlog) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->listen(backlog);
+}
+
+static int nr_socket_local_accept(void *obj, nr_transport_addr *addrp,
+ nr_socket **sockp) {
+ NrSocketBase *sock = static_cast<NrSocketBase *>(obj);
+
+ return sock->accept(addrp, sockp);
+}
+
+// Implement async api
+int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg,
+ char *function,int line) {
+ NrSocketBase *s = static_cast<NrSocketBase *>(sock);
+
+ return s->async_wait(how, cb, cb_arg, function, line);
+}
+
+int NR_async_cancel(NR_SOCKET sock,int how) {
+ NrSocketBase *s = static_cast<NrSocketBase *>(sock);
+
+ return s->cancel(how);
+}
+
+nr_socket_vtbl* NrSocketBase::vtbl() {
+ return &nr_socket_local_vtbl;
+}
diff --git a/media/mtransport/nr_socket_prsock.h b/media/mtransport/nr_socket_prsock.h
new file mode 100644
index 000000000..0883ef7a5
--- /dev/null
+++ b/media/mtransport/nr_socket_prsock.h
@@ -0,0 +1,411 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+/* Some source code here from nICEr. Copyright is:
+
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+// Implementation of nICEr/nr_socket that is tied to the Gecko
+// SocketTransportService.
+
+#ifndef nr_socket_prsock__
+#define nr_socket_prsock__
+
+#include <queue>
+
+#include "nspr.h"
+#include "prio.h"
+
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsASocketHandler.h"
+#include "nsISocketTransportService.h"
+#include "nsXPCOM.h"
+#include "nsIEventTarget.h"
+#include "nsIUDPSocketChild.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+#include "nsITCPSocketCallback.h"
+#include "databuffer.h"
+#include "m_cpp_utils.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/ClearOnShutdown.h"
+
+// Stub declaration for nICEr type
+typedef struct nr_socket_vtbl_ nr_socket_vtbl;
+typedef struct nr_socket_ nr_socket;
+
+#if defined(MOZILLA_INTERNAL_API)
+namespace mozilla {
+namespace dom {
+class TCPSocketChild;
+}
+}
+#endif
+
+namespace mozilla {
+
+namespace net {
+ union NetAddr;
+}
+
+class NrSocketBase {
+public:
+ NrSocketBase() : connect_invoked_(false), poll_flags_(0) {
+ memset(cbs_, 0, sizeof(cbs_));
+ memset(cb_args_, 0, sizeof(cb_args_));
+ memset(&my_addr_, 0, sizeof(my_addr_));
+ }
+ virtual ~NrSocketBase() {}
+
+ // Factory method; will create either an NrSocket, NrUdpSocketIpc, or
+ // NrTcpSocketIpc as appropriate.
+ static int CreateSocket(nr_transport_addr *addr, RefPtr<NrSocketBase> *sock);
+
+ // the nr_socket APIs
+ virtual int create(nr_transport_addr *addr) = 0;
+ virtual int sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) = 0;
+ virtual int recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) = 0;
+ virtual int getaddr(nr_transport_addr *addrp) = 0;
+ virtual void close() = 0;
+ virtual int connect(nr_transport_addr *addr) = 0;
+ virtual int write(const void *msg, size_t len, size_t *written) = 0;
+ virtual int read(void* buf, size_t maxlen, size_t *len) = 0;
+ virtual int listen(int backlog) = 0;
+ virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) = 0;
+
+ // Implementations of the async_event APIs
+ virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line);
+ virtual int cancel(int how);
+
+ // nsISupport reference counted interface
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+ uint32_t poll_flags() {
+ return poll_flags_;
+ }
+
+ virtual nr_socket_vtbl *vtbl(); // To access in test classes.
+
+ static TimeStamp short_term_violation_time();
+ static TimeStamp long_term_violation_time();
+ const nr_transport_addr& my_addr() const {
+ return my_addr_;
+ }
+
+ void fire_callback(int how);
+
+protected:
+
+ bool connect_invoked_;
+ nr_transport_addr my_addr_;
+
+private:
+ NR_async_cb cbs_[NR_ASYNC_WAIT_WRITE + 1];
+ void *cb_args_[NR_ASYNC_WAIT_WRITE + 1];
+ uint32_t poll_flags_;
+};
+
+class NrSocket : public NrSocketBase,
+ public nsASocketHandler {
+public:
+ NrSocket() : fd_(nullptr) {}
+
+ // Implement nsASocket
+ virtual void OnSocketReady(PRFileDesc *fd, int16_t outflags) override;
+ virtual void OnSocketDetached(PRFileDesc *fd) override;
+ virtual void IsLocal(bool *aIsLocal) override;
+ virtual uint64_t ByteCountSent() override { return 0; }
+ virtual uint64_t ByteCountReceived() override { return 0; }
+
+ // nsISupports methods
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // Implementations of the async_event APIs
+ virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line) override;
+ virtual int cancel(int how) override;
+
+
+ // Implementations of the nr_socket APIs
+ virtual int create(nr_transport_addr *addr) override; // (really init, but it's called create)
+ virtual int sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) override;
+ virtual int recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) override;
+ virtual int getaddr(nr_transport_addr *addrp) override;
+ virtual void close() override;
+ virtual int connect(nr_transport_addr *addr) override;
+ virtual int write(const void *msg, size_t len, size_t *written) override;
+ virtual int read(void* buf, size_t maxlen, size_t *len) override;
+ virtual int listen(int backlog) override;
+ virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
+
+protected:
+ virtual ~NrSocket() {
+ if (fd_)
+ PR_Close(fd_);
+ }
+
+ DISALLOW_COPY_ASSIGN(NrSocket);
+
+ PRFileDesc *fd_;
+ nsCOMPtr<nsIEventTarget> ststhread_;
+};
+
+struct nr_udp_message {
+ nr_udp_message(const PRNetAddr &from, nsAutoPtr<DataBuffer> &data)
+ : from(from), data(data) {
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_udp_message);
+
+ PRNetAddr from;
+ nsAutoPtr<DataBuffer> data;
+
+private:
+ ~nr_udp_message() {}
+ DISALLOW_COPY_ASSIGN(nr_udp_message);
+};
+
+class NrSocketIpc : public NrSocketBase {
+public:
+
+ enum NrSocketIpcState {
+ NR_INIT,
+ NR_CONNECTING,
+ NR_CONNECTED,
+ NR_CLOSING,
+ NR_CLOSED,
+ };
+
+ NrSocketIpc(nsIEventTarget* aThread);
+
+protected:
+ nsCOMPtr<nsIEventTarget> sts_thread_;
+ // Note: for UDP PBackground, this is a thread held by SingletonThreadHolder.
+ // For TCP PNecko, this is MainThread (and TCPSocket requires MainThread currently)
+ const nsCOMPtr<nsIEventTarget> io_thread_;
+ virtual ~NrSocketIpc() {};
+
+private:
+ DISALLOW_COPY_ASSIGN(NrSocketIpc);
+};
+
+class NrUdpSocketIpc : public NrSocketIpc {
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrUdpSocketIpc, override)
+
+ NS_IMETHODIMP CallListenerError(const nsACString &message,
+ const nsACString &filename,
+ uint32_t line_number);
+ NS_IMETHODIMP CallListenerReceivedData(const nsACString &host,
+ uint16_t port,
+ const uint8_t *data,
+ uint32_t data_length);
+ NS_IMETHODIMP CallListenerOpened();
+ NS_IMETHODIMP CallListenerConnected();
+ NS_IMETHODIMP CallListenerClosed();
+
+ NrUdpSocketIpc();
+
+ // Implementations of the NrSocketBase APIs
+ virtual int create(nr_transport_addr *addr) override;
+ virtual int sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) override;
+ virtual int recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) override;
+ virtual int getaddr(nr_transport_addr *addrp) override;
+ virtual void close() override;
+ virtual int connect(nr_transport_addr *addr) override;
+ virtual int write(const void *msg, size_t len, size_t *written) override;
+ virtual int read(void* buf, size_t maxlen, size_t *len) override;
+ virtual int listen(int backlog) override;
+ virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
+
+private:
+ virtual ~NrUdpSocketIpc();
+
+ DISALLOW_COPY_ASSIGN(NrUdpSocketIpc);
+
+ nsresult SetAddress(); // Set the local address from parent info.
+
+ // Main or private thread executors of the NrSocketBase APIs
+ void create_i(const nsACString &host, const uint16_t port);
+ void connect_i(const nsACString &host, const uint16_t port);
+ void sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
+ void close_i();
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+ static void release_child_i(nsIUDPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
+ static void release_use_s();
+#endif
+ // STS thread executor
+ void recv_callback_s(RefPtr<nr_udp_message> msg);
+
+ ReentrantMonitor monitor_; // protects err_and state_
+ bool err_;
+ NrSocketIpcState state_;
+
+ std::queue<RefPtr<nr_udp_message>> received_msgs_;
+
+ RefPtr<nsIUDPSocketChild> socket_child_; // only accessed from the io_thread
+};
+
+// The socket child holds onto one of these, which just passes callbacks
+// through and makes sure the ref to the NrSocketIpc is released on STS.
+class NrUdpSocketIpcProxy : public nsIUDPSocketInternal {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIUDPSOCKETINTERNAL
+
+ nsresult Init(const RefPtr<NrUdpSocketIpc>& socket);
+
+private:
+ virtual ~NrUdpSocketIpcProxy();
+
+ RefPtr<NrUdpSocketIpc> socket_;
+ nsCOMPtr<nsIEventTarget> sts_thread_;
+};
+
+struct nr_tcp_message {
+ explicit nr_tcp_message(nsAutoPtr<DataBuffer> &data)
+ : read_bytes(0)
+ , data(data) {
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_tcp_message);
+
+ const uint8_t *reading_pointer() const {
+ return data->data() + read_bytes;
+ }
+
+ size_t unread_bytes() const {
+ return data->len() - read_bytes;
+ }
+
+ size_t read_bytes;
+
+private:
+ ~nr_tcp_message() {}
+ DISALLOW_COPY_ASSIGN(nr_tcp_message);
+
+ nsAutoPtr<DataBuffer> data;
+};
+
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+class NrTcpSocketIpc : public NrSocketIpc,
+ public nsITCPSocketCallback {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITCPSOCKETCALLBACK
+
+ explicit NrTcpSocketIpc(nsIThread* aThread);
+
+ // Implementations of the NrSocketBase APIs
+ virtual int create(nr_transport_addr *addr) override;
+ virtual int sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) override;
+ virtual int recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) override;
+ virtual int getaddr(nr_transport_addr *addrp) override;
+ virtual void close() override;
+ virtual int connect(nr_transport_addr *addr) override;
+ virtual int write(const void *msg, size_t len, size_t *written) override;
+ virtual int read(void* buf, size_t maxlen, size_t *len) override;
+ virtual int listen(int backlog) override;
+ virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
+
+private:
+ class TcpSocketReadyRunner;
+ DISALLOW_COPY_ASSIGN(NrTcpSocketIpc);
+ virtual ~NrTcpSocketIpc();
+
+ // Main thread executors of the NrSocketBase APIs
+ void connect_i(const nsACString &remote_addr,
+ uint16_t remote_port,
+ const nsACString &local_addr,
+ uint16_t local_port);
+ void write_i(nsAutoPtr<InfallibleTArray<uint8_t>> buf,
+ uint32_t tracking_number);
+ void close_i();
+
+ static void release_child_i(dom::TCPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
+
+ // STS thread executor
+ void message_sent_s(uint32_t bufferedAmount, uint32_t tracking_number);
+ void recv_message_s(nr_tcp_message *msg);
+ void update_state_s(NrSocketIpcState next_state);
+ void maybe_post_socket_ready();
+
+ // Accessed from UpdateReadyState (not sts_thread) to avoid sending
+ // runnables when not needed
+ NrSocketIpcState mirror_state_;
+
+ // variables that can only be accessed on STS.
+ NrSocketIpcState state_;
+ std::queue<RefPtr<nr_tcp_message>> msg_queue_;
+ uint32_t buffered_bytes_;
+ uint32_t tracking_number_;
+ std::deque<size_t> writes_in_flight_;
+
+ // main thread.
+ RefPtr<dom::TCPSocketChild> socket_child_;
+};
+#endif
+
+int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
+ nr_transport_addr *addr,
+ int protocol);
+int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
+ nr_transport_addr *addr,
+ int protocol, int keep);
+int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
+ nsACString *host, int32_t *port);
+} // close namespace
+#endif
diff --git a/media/mtransport/nr_timer.cpp b/media/mtransport/nr_timer.cpp
new file mode 100644
index 000000000..a1f9d709c
--- /dev/null
+++ b/media/mtransport/nr_timer.cpp
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original code by: ekr@rtfm.com
+
+// Implementation of the NR timer interface
+
+// Some code here copied from nrappkit. The license was.
+
+/**
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Sun Feb 22 19:35:24 2004
+ */
+
+#include <string>
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+#include "nsNetCID.h"
+#include "runnable_utils.h"
+#include "mozilla/DebugOnly.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "async_timer.h"
+}
+
+
+namespace mozilla {
+
+class nrappkitCallback {
+ public:
+ nrappkitCallback(NR_async_cb cb, void *cb_arg,
+ const char *function, int line)
+ : cb_(cb), cb_arg_(cb_arg), function_(function), line_(line) {
+ }
+ virtual ~nrappkitCallback() {}
+
+ virtual void Cancel() = 0;
+
+protected:
+ /* additional members */
+ NR_async_cb cb_;
+ void *cb_arg_;
+ std::string function_;
+ int line_;
+};
+
+class nrappkitTimerCallback : public nrappkitCallback,
+ public nsITimerCallback {
+ public:
+ // We're going to release ourself in the callback, so we need to be threadsafe
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+ nrappkitTimerCallback(NR_async_cb cb, void *cb_arg,
+ const char *function, int line)
+ : nrappkitCallback(cb, cb_arg, function, line),
+ timer_(nullptr) {}
+
+ void SetTimer(already_AddRefed<nsITimer>&& timer) {
+ timer_ = timer;
+ }
+
+ virtual void Cancel() override {
+ AddRef(); // Cancelling the timer causes the callback it holds to
+ // be released. AddRef() keeps us alive.
+ timer_->Cancel();
+ timer_ = nullptr;
+ Release(); // Will cause deletion of this object.
+ }
+
+ private:
+ nsCOMPtr<nsITimer> timer_;
+ virtual ~nrappkitTimerCallback() {}
+};
+
+NS_IMPL_ISUPPORTS(nrappkitTimerCallback, nsITimerCallback)
+
+NS_IMETHODIMP nrappkitTimerCallback::Notify(nsITimer *timer) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "Timer callback fired (set in %s:%d)",
+ function_.c_str(), line_);
+ MOZ_RELEASE_ASSERT(timer == timer_);
+ cb_(0, 0, cb_arg_);
+
+ // Allow the timer to go away.
+ timer_ = nullptr;
+ return NS_OK;
+}
+
+class nrappkitScheduledCallback : public nrappkitCallback {
+ public:
+
+ nrappkitScheduledCallback(NR_async_cb cb, void *cb_arg,
+ const char *function, int line)
+ : nrappkitCallback(cb, cb_arg, function, line) {}
+
+ void Run() {
+ if (cb_) {
+ cb_(0, 0, cb_arg_);
+ }
+ }
+
+ virtual void Cancel() override {
+ cb_ = nullptr;
+ }
+
+ ~nrappkitScheduledCallback() {}
+};
+
+} // close namespace
+
+
+using namespace mozilla;
+
+static nsCOMPtr<nsIEventTarget> GetSTSThread() {
+ nsresult rv;
+
+ nsCOMPtr<nsIEventTarget> sts_thread;
+
+ sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ return sts_thread;
+}
+
+// These timers must only be used from the STS thread.
+// This function is a helper that enforces that.
+static void CheckSTSThread() {
+ DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread();
+
+ ASSERT_ON_THREAD(sts_thread.value);
+}
+
+static int nr_async_timer_set_zero(NR_async_cb cb, void *arg,
+ char *func, int l,
+ nrappkitCallback **handle) {
+ nrappkitScheduledCallback* callback(new nrappkitScheduledCallback(
+ cb, arg, func, l));
+
+ nsresult rv = GetSTSThread()->Dispatch(WrapRunnable(
+ nsAutoPtr<nrappkitScheduledCallback>(callback),
+ &nrappkitScheduledCallback::Run),
+ NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv))
+ return R_FAILED;
+
+ *handle = callback;
+
+ // On exit to this function, the only strong reference to callback is in
+ // the Runnable. Because we are redispatching to the same thread,
+ // this is always safe.
+ return 0;
+}
+
+static int nr_async_timer_set_nonzero(int timeout, NR_async_cb cb, void *arg,
+ char *func, int l,
+ nrappkitCallback **handle) {
+ nsresult rv;
+ CheckSTSThread();
+
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return(R_FAILED);
+ }
+
+ nrappkitTimerCallback* callback =
+ new nrappkitTimerCallback(cb, arg, func, l);
+ rv = timer->InitWithCallback(callback, timeout, nsITimer::TYPE_ONE_SHOT);
+ if (NS_FAILED(rv)) {
+ return R_FAILED;
+ }
+
+ // Move the ownership of the timer to the callback object, which holds the
+ // timer alive per spec.
+ callback->SetTimer(timer.forget());
+
+ *handle = callback;
+
+ return 0;
+}
+
+int NR_async_timer_set(int timeout, NR_async_cb cb, void *arg,
+ char *func, int l, void **handle) {
+ CheckSTSThread();
+
+ nrappkitCallback *callback;
+ int r;
+
+ if (!timeout) {
+ r = nr_async_timer_set_zero(cb, arg, func, l, &callback);
+ } else {
+ r = nr_async_timer_set_nonzero(timeout, cb, arg, func, l, &callback);
+ }
+
+ if (r)
+ return r;
+
+ if (handle)
+ *handle = callback;
+
+ return 0;
+}
+
+int NR_async_schedule(NR_async_cb cb, void *arg, char *func, int l) {
+ // No need to check the thread because we check it next in the
+ // timer set.
+ return NR_async_timer_set(0, cb, arg, func, l, nullptr);
+}
+
+int NR_async_timer_cancel(void *handle) {
+ // Check for the handle being nonzero because sometimes we get
+ // no-op cancels that aren't on the STS thread. This can be
+ // non-racy as long as the upper-level code is careful.
+ if (!handle)
+ return 0;
+
+ CheckSTSThread();
+
+ nrappkitCallback* callback = static_cast<nrappkitCallback *>(handle);
+ callback->Cancel();
+
+ return 0;
+}
+
diff --git a/media/mtransport/nricectx.cpp b/media/mtransport/nricectx.cpp
new file mode 100644
index 000000000..abd8870ee
--- /dev/null
+++ b/media/mtransport/nricectx.cpp
@@ -0,0 +1,1017 @@
+/* -*- mode: c++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Original author: ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string>
+#include <vector>
+
+#include "mozilla/UniquePtr.h"
+
+#include "logging.h"
+#include "nspr.h"
+#include "nss.h"
+#include "pk11pub.h"
+#include "plbase64.h"
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsError.h"
+#include "nsIEventTarget.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "ScopedNSSTypes.h"
+#include "runnable_utils.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "registry.h"
+#include "async_timer.h"
+#include "r_crc32.h"
+#include "r_memory.h"
+#include "ice_reg.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "nr_proxy_tunnel.h"
+#include "stun_client_ctx.h"
+#include "stun_reg.h"
+#include "stun_server_ctx.h"
+#include "ice_codeword.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_handler.h"
+}
+
+// Local includes
+#include "nricectx.h"
+#include "nricemediastream.h"
+#include "nr_socket_prsock.h"
+#include "nrinterfaceprioritizer.h"
+#include "rlogconnector.h"
+#include "test_nr_socket.h"
+
+namespace mozilla {
+
+TimeStamp nr_socket_short_term_violation_time() {
+ return NrSocketBase::short_term_violation_time();
+}
+
+TimeStamp nr_socket_long_term_violation_time() {
+ return NrSocketBase::long_term_violation_time();
+}
+
+MOZ_MTLOG_MODULE("mtransport")
+
+const char kNrIceTransportUdp[] = "udp";
+const char kNrIceTransportTcp[] = "tcp";
+
+static bool initialized = false;
+
+// Implement NSPR-based crypto algorithms
+static int nr_crypto_nss_random_bytes(UCHAR *buf, int len) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot)
+ return R_INTERNAL;
+
+ SECStatus rv = PK11_GenerateRandomOnSlot(slot, buf, len);
+ if (rv != SECSuccess)
+ return R_INTERNAL;
+
+ return 0;
+}
+
+static int nr_crypto_nss_hmac(UCHAR *key, int keyl, UCHAR *buf, int bufl,
+ UCHAR *result) {
+ CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
+ PK11SlotInfo *slot = 0;
+ MOZ_ASSERT(keyl > 0);
+ SECItem keyi = { siBuffer, key, static_cast<unsigned int>(keyl)};
+ PK11SymKey *skey = 0;
+ PK11Context *hmac_ctx = 0;
+ SECStatus status;
+ unsigned int hmac_len;
+ SECItem param = { siBuffer, nullptr, 0 };
+ int err = R_INTERNAL;
+
+ slot = PK11_GetInternalKeySlot();
+ if (!slot)
+ goto abort;
+
+ skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap,
+ CKA_SIGN, &keyi, nullptr);
+ if (!skey)
+ goto abort;
+
+
+ hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN,
+ skey, &param);
+ if (!hmac_ctx)
+ goto abort;
+
+ status = PK11_DigestBegin(hmac_ctx);
+ if (status != SECSuccess)
+ goto abort;
+
+ status = PK11_DigestOp(hmac_ctx, buf, bufl);
+ if (status != SECSuccess)
+ goto abort;
+
+ status = PK11_DigestFinal(hmac_ctx, result, &hmac_len, 20);
+ if (status != SECSuccess)
+ goto abort;
+
+ MOZ_ASSERT(hmac_len == 20);
+
+ err = 0;
+
+ abort:
+ if(hmac_ctx) PK11_DestroyContext(hmac_ctx, PR_TRUE);
+ if (skey) PK11_FreeSymKey(skey);
+ if (slot) PK11_FreeSlot(slot);
+
+ return err;
+}
+
+static int nr_crypto_nss_md5(UCHAR *buf, int bufl, UCHAR *result) {
+ int err = R_INTERNAL;
+ SECStatus rv;
+
+ const SECHashObject *ho = HASH_GetHashObject(HASH_AlgMD5);
+ MOZ_ASSERT(ho);
+ if (!ho)
+ goto abort;
+
+ MOZ_ASSERT(ho->length == 16);
+
+ rv = HASH_HashBuf(ho->type, result, buf, bufl);
+ if (rv != SECSuccess)
+ goto abort;
+
+ err = 0;
+abort:
+ return err;
+}
+
+static nr_ice_crypto_vtbl nr_ice_crypto_nss_vtbl = {
+ nr_crypto_nss_random_bytes,
+ nr_crypto_nss_hmac,
+ nr_crypto_nss_md5
+};
+
+nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server *server) const {
+ int r;
+
+ memset(server, 0, sizeof(nr_ice_stun_server));
+ if (transport_ == kNrIceTransportUdp) {
+ server->transport = IPPROTO_UDP;
+ } else if (transport_ == kNrIceTransportTcp) {
+ server->transport = IPPROTO_TCP;
+ } else {
+ MOZ_MTLOG(ML_ERROR, "Unsupported STUN server transport: " << transport_);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (has_addr_) {
+ r = nr_praddr_to_transport_addr(&addr_, &server->u.addr,
+ server->transport, 0);
+ if (r) {
+ return NS_ERROR_FAILURE;
+ }
+ server->type=NR_ICE_STUN_SERVER_TYPE_ADDR;
+ }
+ else {
+ MOZ_ASSERT(sizeof(server->u.dnsname.host) > host_.size());
+ PL_strncpyz(server->u.dnsname.host, host_.c_str(),
+ sizeof(server->u.dnsname.host));
+ server->u.dnsname.port = port_;
+ server->type=NR_ICE_STUN_SERVER_TYPE_DNSNAME;
+ }
+
+ return NS_OK;
+}
+
+
+nsresult NrIceTurnServer::ToNicerTurnStruct(nr_ice_turn_server *server) const {
+ memset(server, 0, sizeof(nr_ice_turn_server));
+
+ nsresult rv = ToNicerStunStruct(&server->turn_server);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!(server->username=r_strdup(username_.c_str())))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // TODO(ekr@rtfm.com): handle non-ASCII passwords somehow?
+ // STUN requires they be SASLpreped, but we don't know if
+ // they are at this point.
+
+ // C++03 23.2.4, Paragraph 1 stipulates that the elements
+ // in std::vector must be contiguous, and can therefore be
+ // used as input to functions expecting C arrays.
+ int r = r_data_create(&server->password,
+ const_cast<UCHAR *>(&password_[0]),
+ password_.size());
+ if (r) {
+ RFREE(server->username);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NrIceCtx::NrIceCtx(const std::string& name,
+ bool offerer,
+ Policy policy)
+ : connection_state_(ICE_CTX_INIT),
+ gathering_state_(ICE_CTX_GATHER_INIT),
+ name_(name),
+ offerer_(offerer),
+ ice_controlling_set_(false),
+ streams_(),
+ ctx_(nullptr),
+ peer_(nullptr),
+ ice_handler_vtbl_(nullptr),
+ ice_handler_(nullptr),
+ trickle_(true),
+ policy_(policy),
+ nat_ (nullptr) {
+ // XXX: offerer_ will be used eventually; placate clang in the meantime.
+ (void)offerer_;
+}
+
+// Handler callbacks
+int NrIceCtx::select_pair(void *obj,nr_ice_media_stream *stream,
+ int component_id, nr_ice_cand_pair **potentials,
+ int potential_ct) {
+ MOZ_MTLOG(ML_DEBUG, "select pair called: potential_ct = "
+ << potential_ct);
+
+ return 0;
+}
+
+int NrIceCtx::stream_ready(void *obj, nr_ice_media_stream *stream) {
+ MOZ_MTLOG(ML_DEBUG, "stream_ready called");
+
+ // Get the ICE ctx.
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+
+ RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
+
+ // Streams which do not exist should never be ready.
+ MOZ_ASSERT(s);
+
+ s->Ready();
+
+ return 0;
+}
+
+int NrIceCtx::stream_failed(void *obj, nr_ice_media_stream *stream) {
+ MOZ_MTLOG(ML_DEBUG, "stream_failed called");
+
+ // Get the ICE ctx
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+ RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
+
+ // Streams which do not exist should never fail.
+ MOZ_ASSERT(s);
+
+ ctx->SetConnectionState(ICE_CTX_FAILED);
+ s -> SignalFailed(s);
+ return 0;
+}
+
+int NrIceCtx::ice_checking(void *obj, nr_ice_peer_ctx *pctx) {
+ MOZ_MTLOG(ML_DEBUG, "ice_checking called");
+
+ // Get the ICE ctx
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+
+ ctx->SetConnectionState(ICE_CTX_CHECKING);
+
+ return 0;
+}
+
+int NrIceCtx::ice_connected(void *obj, nr_ice_peer_ctx *pctx) {
+ MOZ_MTLOG(ML_DEBUG, "ice_connected called");
+
+ // Get the ICE ctx
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+
+ // This is called even on failed contexts.
+ if (ctx->connection_state() != ICE_CTX_FAILED) {
+ ctx->SetConnectionState(ICE_CTX_CONNECTED);
+ }
+
+ return 0;
+}
+
+int NrIceCtx::ice_disconnected(void *obj, nr_ice_peer_ctx *pctx) {
+ MOZ_MTLOG(ML_DEBUG, "ice_disconnected called");
+
+ // Get the ICE ctx
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+
+ ctx->SetConnectionState(ICE_CTX_DISCONNECTED);
+
+ return 0;
+}
+
+int NrIceCtx::msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
+ nr_ice_media_stream *stream, int component_id,
+ UCHAR *msg, int len) {
+ // Get the ICE ctx
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(obj);
+ RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
+
+ // Streams which do not exist should never have packets.
+ MOZ_ASSERT(s);
+
+ s->SignalPacketReceived(s, component_id, msg, len);
+
+ return 0;
+}
+
+void NrIceCtx::trickle_cb(void *arg, nr_ice_ctx *ice_ctx,
+ nr_ice_media_stream *stream,
+ int component_id,
+ nr_ice_candidate *candidate) {
+ // Get the ICE ctx
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
+ RefPtr<NrIceMediaStream> s = ctx->FindStream(stream);
+
+ if (!s) {
+ // This stream has been removed because it is inactive
+ return;
+ }
+
+ // Format the candidate.
+ char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE];
+ int r = nr_ice_format_candidate_attribute(candidate, candidate_str,
+ sizeof(candidate_str));
+ MOZ_ASSERT(!r);
+ if (r)
+ return;
+
+ MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate "
+ << candidate_str);
+
+ s->SignalCandidate(s, candidate_str);
+}
+
+
+void
+NrIceCtx::InitializeGlobals(bool allow_loopback,
+ bool tcp_enabled,
+ bool allow_link_local) {
+ // Initialize the crypto callbacks and logging stuff
+ if (!initialized) {
+ NR_reg_init(NR_REG_MODE_LOCAL);
+ nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
+ initialized = true;
+
+ // Set the priorites for candidate type preferences.
+ // These numbers come from RFC 5245 S. 4.1.2.2
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_SRV_RFLX, 100);
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_PEER_RFLX, 110);
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_HOST, 126);
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED, 5);
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP, 99);
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP, 109);
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_HOST_TCP, 125);
+ NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED_TCP, 0);
+
+ int32_t stun_client_maximum_transmits = 7;
+ int32_t ice_trickle_grace_period = 5000;
+ int32_t ice_tcp_so_sock_count = 3;
+ int32_t ice_tcp_listen_backlog = 10;
+ nsAutoCString force_net_interface;
+ nsresult res;
+ nsCOMPtr<nsIPrefService> prefs =
+ do_GetService("@mozilla.org/preferences-service;1", &res);
+
+ if (NS_SUCCEEDED(res)) {
+ nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
+ if (branch) {
+ branch->GetIntPref(
+ "media.peerconnection.ice.stun_client_maximum_transmits",
+ &stun_client_maximum_transmits);
+ branch->GetIntPref(
+ "media.peerconnection.ice.trickle_grace_period",
+ &ice_trickle_grace_period);
+ branch->GetIntPref(
+ "media.peerconnection.ice.tcp_so_sock_count",
+ &ice_tcp_so_sock_count);
+ branch->GetIntPref(
+ "media.peerconnection.ice.tcp_listen_backlog",
+ &ice_tcp_listen_backlog);
+ branch->GetCharPref(
+ "media.peerconnection.ice.force_interface",
+ getter_Copies(force_net_interface));
+ }
+ }
+
+ NR_reg_set_uint4((char *)"stun.client.maximum_transmits",
+ stun_client_maximum_transmits);
+ NR_reg_set_uint4((char *)NR_ICE_REG_TRICKLE_GRACE_PERIOD,
+ ice_trickle_grace_period);
+ NR_reg_set_int4((char *)NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT,
+ ice_tcp_so_sock_count);
+ NR_reg_set_int4((char *)NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG,
+ ice_tcp_listen_backlog);
+
+ NR_reg_set_char((char *)NR_ICE_REG_ICE_TCP_DISABLE, !tcp_enabled);
+
+ if (allow_loopback) {
+ NR_reg_set_char((char *)NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, 1);
+ }
+
+ if (allow_link_local) {
+ NR_reg_set_char((char *)NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, 1);
+ }
+ if (force_net_interface.Length() > 0) {
+ // Stupid cast.... but needed
+ const nsCString& flat = PromiseFlatCString(static_cast<nsACString&>(force_net_interface));
+ NR_reg_set_string((char *)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME, const_cast<char*>(flat.get()));
+ }
+ }
+}
+
+std::string
+NrIceCtx::GetNewUfrag()
+{
+ char* ufrag;
+ int r;
+
+ if ((r=nr_ice_get_new_ice_ufrag(&ufrag))) {
+ MOZ_CRASH("Unable to get new ice ufrag");
+ return "";
+ }
+
+ std::string ufragStr = ufrag;
+ RFREE(ufrag);
+
+ return ufragStr;
+}
+
+std::string
+NrIceCtx::GetNewPwd()
+{
+ char* pwd;
+ int r;
+
+ if ((r=nr_ice_get_new_ice_pwd(&pwd))) {
+ MOZ_CRASH("Unable to get new ice pwd");
+ return "";
+ }
+
+ std::string pwdStr = pwd;
+ RFREE(pwd);
+
+ return pwdStr;
+}
+
+bool
+NrIceCtx::Initialize()
+{
+ std::string ufrag = GetNewUfrag();
+ std::string pwd = GetNewPwd();
+
+ return Initialize(ufrag, pwd);
+}
+
+bool
+NrIceCtx::Initialize(const std::string& ufrag,
+ const std::string& pwd)
+{
+ MOZ_ASSERT(!ufrag.empty());
+ MOZ_ASSERT(!pwd.empty());
+ if (ufrag.empty() || pwd.empty()) {
+ return false;
+ }
+
+ // Create the ICE context
+ int r;
+
+ UINT4 flags = offerer_ ? NR_ICE_CTX_FLAGS_OFFERER:
+ NR_ICE_CTX_FLAGS_ANSWERER;
+ flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
+ switch (policy_) {
+ case ICE_POLICY_RELAY:
+ flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY;
+ break;
+ case ICE_POLICY_NO_HOST:
+ flags |= NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES;
+ break;
+ case ICE_POLICY_ALL:
+ break;
+ }
+
+ r = nr_ice_ctx_create_with_credentials(const_cast<char *>(name_.c_str()),
+ flags,
+ const_cast<char *>(ufrag.c_str()),
+ const_cast<char *>(pwd.c_str()),
+ &ctx_);
+ MOZ_ASSERT(ufrag == ctx_->ufrag);
+ MOZ_ASSERT(pwd == ctx_->pwd);
+
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'");
+ return false;
+ }
+
+ nr_interface_prioritizer *prioritizer = CreateInterfacePrioritizer();
+ if (!prioritizer) {
+ MOZ_MTLOG(LogLevel::Error, "Couldn't create interface prioritizer.");
+ return false;
+ }
+
+ r = nr_ice_ctx_set_interface_prioritizer(ctx_, prioritizer);
+ if (r) {
+ MOZ_MTLOG(LogLevel::Error, "Couldn't set interface prioritizer.");
+ return false;
+ }
+
+ if (generating_trickle()) {
+ r = nr_ice_ctx_set_trickle_cb(ctx_, &NrIceCtx::trickle_cb, this);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name_ << "'");
+ return false;
+ }
+ }
+
+ nsCString mapping_type;
+ nsCString filtering_type;
+ bool block_udp = false;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> pref_service =
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIPrefBranch> pref_branch;
+ rv = pref_service->GetBranch(nullptr, getter_AddRefs(pref_branch));
+ if (NS_SUCCEEDED(rv)) {
+ rv = pref_branch->GetCharPref(
+ "media.peerconnection.nat_simulator.mapping_type",
+ getter_Copies(mapping_type));
+ rv = pref_branch->GetCharPref(
+ "media.peerconnection.nat_simulator.filtering_type",
+ getter_Copies(filtering_type));
+ rv = pref_branch->GetBoolPref(
+ "media.peerconnection.nat_simulator.block_udp",
+ &block_udp);
+ }
+ }
+
+ if (!mapping_type.IsEmpty() && !filtering_type.IsEmpty()) {
+ MOZ_MTLOG(ML_DEBUG, "NAT filtering type: " << filtering_type.get());
+ MOZ_MTLOG(ML_DEBUG, "NAT mapping type: " << mapping_type.get());
+ TestNat* test_nat = new TestNat;
+ test_nat->filtering_type_ = TestNat::ToNatBehavior(filtering_type.get());
+ test_nat->mapping_type_ = TestNat::ToNatBehavior(mapping_type.get());
+ test_nat->block_udp_ = block_udp;
+ test_nat->enabled_ = true;
+ SetNat(test_nat);
+ }
+
+ // Create the handler objects
+ ice_handler_vtbl_ = new nr_ice_handler_vtbl();
+ ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair;
+ ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready;
+ ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed;
+ ice_handler_vtbl_->ice_connected = &NrIceCtx::ice_connected;
+ ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd;
+ ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking;
+ ice_handler_vtbl_->ice_disconnected = &NrIceCtx::ice_disconnected;
+
+ ice_handler_ = new nr_ice_handler();
+ ice_handler_->vtbl = ice_handler_vtbl_;
+ ice_handler_->obj = this;
+
+ // Create the peer ctx. Because we do not support parallel forking, we
+ // only have one peer ctx.
+ std::string peer_name = name_ + ":default";
+ r = nr_ice_peer_ctx_create(ctx_, ice_handler_,
+ const_cast<char *>(peer_name.c_str()),
+ &peer_);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name_ << "'");
+ return false;
+ }
+
+ sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+ if (!NS_SUCCEEDED(rv))
+ return false;
+
+ return true;
+}
+
+int NrIceCtx::SetNat(const RefPtr<TestNat>& aNat) {
+ nat_ = aNat;
+ nr_socket_factory *fac;
+ int r = nat_->create_socket_factory(&fac);
+ if (r) {
+ return r;
+ }
+ nr_ice_ctx_set_socket_factory(ctx_, fac);
+ return 0;
+}
+
+// ONLY USE THIS FOR TESTING. Will cause totally unpredictable and possibly very
+// bad effects if ICE is still live.
+void NrIceCtx::internal_DeinitializeGlobal() {
+ NR_reg_del((char *)"stun");
+ NR_reg_del((char *)"ice");
+ RLogConnector::DestroyInstance();
+ nr_crypto_vtbl = nullptr;
+ initialized = false;
+}
+
+void NrIceCtx::internal_SetTimerAccelarator(int divider) {
+ ctx_->test_timer_divider = divider;
+}
+
+NrIceCtx::~NrIceCtx() {
+ MOZ_MTLOG(ML_DEBUG, "Destroying ICE ctx '" << name_ <<"'");
+ for (auto stream = streams_.begin(); stream != streams_.end(); stream++) {
+ if (*stream) {
+ (*stream)->Close();
+ }
+ }
+ nr_ice_peer_ctx_destroy(&peer_);
+ nr_ice_ctx_destroy(&ctx_);
+ delete ice_handler_vtbl_;
+ delete ice_handler_;
+}
+
+void
+NrIceCtx::SetStream(size_t index, NrIceMediaStream* stream) {
+ if (index >= streams_.size()) {
+ streams_.resize(index + 1);
+ }
+
+ RefPtr<NrIceMediaStream> oldStream(streams_[index]);
+ streams_[index] = stream;
+
+ if (oldStream) {
+ oldStream->Close();
+ }
+}
+
+std::string NrIceCtx::ufrag() const {
+ return ctx_->ufrag;
+}
+
+std::string NrIceCtx::pwd() const {
+ return ctx_->pwd;
+}
+
+void NrIceCtx::destroy_peer_ctx() {
+ nr_ice_peer_ctx_destroy(&peer_);
+}
+
+nsresult NrIceCtx::SetControlling(Controlling controlling) {
+ if (!ice_controlling_set_) {
+ peer_->controlling = (controlling == ICE_CONTROLLING)? 1 : 0;
+ ice_controlling_set_ = true;
+
+ MOZ_MTLOG(ML_DEBUG, "ICE ctx " << name_ << " setting controlling to" <<
+ controlling);
+ }
+ return NS_OK;
+}
+
+NrIceCtx::Controlling NrIceCtx::GetControlling() {
+ return (peer_->controlling) ? ICE_CONTROLLING : ICE_CONTROLLED;
+}
+
+nsresult NrIceCtx::SetPolicy(Policy policy) {
+ policy_ = policy;
+ return NS_OK;
+}
+
+nsresult NrIceCtx::SetStunServers(const std::vector<NrIceStunServer>&
+ stun_servers) {
+ if (stun_servers.empty())
+ return NS_OK;
+
+ auto servers = MakeUnique<nr_ice_stun_server[]>(stun_servers.size());
+
+ for (size_t i=0; i < stun_servers.size(); ++i) {
+ nsresult rv = stun_servers[i].ToNicerStunStruct(&servers[i]);
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set STUN server for '" << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ int r = nr_ice_ctx_set_stun_servers(ctx_, servers.get(), stun_servers.size());
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set STUN server for '" << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// TODO(ekr@rtfm.com): This is just SetStunServers with s/Stun/Turn
+// Could we do a template or something?
+nsresult NrIceCtx::SetTurnServers(const std::vector<NrIceTurnServer>&
+ turn_servers) {
+ if (turn_servers.empty())
+ return NS_OK;
+
+ auto servers = MakeUnique<nr_ice_turn_server[]>(turn_servers.size());
+
+ for (size_t i=0; i < turn_servers.size(); ++i) {
+ nsresult rv = turn_servers[i].ToNicerTurnStruct(&servers[i]);
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set TURN server for '" << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ int r = nr_ice_ctx_set_turn_servers(ctx_, servers.get(), turn_servers.size());
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set TURN server for '" << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ // TODO(ekr@rtfm.com): This leaks the username/password. Need to free that.
+
+ return NS_OK;
+}
+
+nsresult NrIceCtx::SetResolver(nr_resolver *resolver) {
+ int r = nr_ice_ctx_set_resolver(ctx_, resolver);
+
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set resolver for '" << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult NrIceCtx::SetProxyServer(const NrIceProxyServer& proxy_server) {
+ int r,_status;
+ nr_proxy_tunnel_config *config = nullptr;
+ nr_socket_wrapper_factory *wrapper = nullptr;
+
+ if ((r = nr_proxy_tunnel_config_create(&config))) {
+ ABORT(r);
+ }
+
+ if ((r = nr_proxy_tunnel_config_set_proxy(config,
+ proxy_server.host().c_str(),
+ proxy_server.port()))) {
+ ABORT(r);
+ }
+
+ if ((r = nr_proxy_tunnel_config_set_resolver(config, ctx_->resolver))) {
+ ABORT(r);
+ }
+
+ if ((r = nr_socket_wrapper_factory_proxy_tunnel_create(config, &wrapper))) {
+ MOZ_MTLOG(LogLevel::Error, "Couldn't create proxy tunnel wrapper.");
+ ABORT(r);
+ }
+
+ // nr_ice_ctx will own the wrapper after this call
+ if ((r = nr_ice_ctx_set_turn_tcp_socket_wrapper(ctx_, wrapper))) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set proxy for '" << name_ << "': " << r);
+ ABORT(r);
+ }
+
+ _status = 0;
+abort:
+ nr_proxy_tunnel_config_destroy(&config);
+ if (_status) {
+ nr_socket_wrapper_factory_destroy(&wrapper);
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+nsresult NrIceCtx::StartGathering(bool default_route_only, bool proxy_only) {
+ ASSERT_ON_THREAD(sts_target_);
+ SetGatheringState(ICE_CTX_GATHER_STARTED);
+
+ if (default_route_only) {
+ nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS);
+ } else {
+ nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS);
+ }
+
+ if (proxy_only) {
+ nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_PROXY);
+ } else {
+ nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_PROXY);
+ }
+
+ // This might start gathering for the first time, or again after
+ // renegotiation, or might do nothing at all if gathering has already
+ // finished.
+ int r = nr_ice_gather(ctx_, &NrIceCtx::gather_cb, this);
+
+ if (!r) {
+ SetGatheringState(ICE_CTX_GATHER_COMPLETE);
+ } else if (r != R_WOULDBLOCK) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't gather ICE candidates for '"
+ << name_ << "', error=" << r);
+ SetConnectionState(ICE_CTX_FAILED);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+RefPtr<NrIceMediaStream> NrIceCtx::FindStream(
+ nr_ice_media_stream *stream) {
+ for (size_t i=0; i<streams_.size(); ++i) {
+ if (streams_[i] && (streams_[i]->stream() == stream)) {
+ return streams_[i];
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector<std::string> NrIceCtx::GetGlobalAttributes() {
+ char **attrs = 0;
+ int attrct;
+ int r;
+ std::vector<std::string> ret;
+
+ r = nr_ice_get_global_attributes(ctx_, &attrs, &attrct);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get ufrag and password for '"
+ << name_ << "'");
+ return ret;
+ }
+
+ for (int i=0; i<attrct; i++) {
+ ret.push_back(std::string(attrs[i]));
+ RFREE(attrs[i]);
+ }
+ RFREE(attrs);
+
+ return ret;
+}
+
+nsresult NrIceCtx::ParseGlobalAttributes(std::vector<std::string> attrs) {
+ std::vector<char *> attrs_in;
+
+ for (size_t i=0; i<attrs.size(); ++i) {
+ attrs_in.push_back(const_cast<char *>(attrs[i].c_str()));
+ }
+
+ int r = nr_ice_peer_ctx_parse_global_attributes(peer_,
+ attrs_in.size() ?
+ &attrs_in[0] : nullptr,
+ attrs_in.size());
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't parse global attributes for "
+ << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult NrIceCtx::StartChecks() {
+ int r;
+
+ r=nr_ice_peer_ctx_pair_candidates(peer_);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't pair candidates on "
+ << name_ << "'");
+ SetConnectionState(ICE_CTX_FAILED);
+ return NS_ERROR_FAILURE;
+ }
+
+ r = nr_ice_peer_ctx_start_checks2(peer_,1);
+ if (r) {
+ if (r == R_NOT_FOUND) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't start peer checks on "
+ << name_ << "' assuming trickle ICE");
+ } else {
+ MOZ_MTLOG(ML_ERROR, "Couldn't start peer checks on "
+ << name_ << "'");
+ SetConnectionState(ICE_CTX_FAILED);
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+
+void NrIceCtx::gather_cb(NR_SOCKET s, int h, void *arg) {
+ NrIceCtx *ctx = static_cast<NrIceCtx *>(arg);
+
+ ctx->SetGatheringState(ICE_CTX_GATHER_COMPLETE);
+}
+
+nsresult NrIceCtx::Finalize() {
+ int r = nr_ice_ctx_finalize(ctx_, peer_);
+
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't finalize "
+ << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void NrIceCtx::SetConnectionState(ConnectionState state) {
+ if (state == connection_state_)
+ return;
+
+ MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): state " <<
+ connection_state_ << "->" << state);
+ connection_state_ = state;
+
+ if (connection_state_ == ICE_CTX_FAILED) {
+ MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): dumping r_log ringbuffer... ");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ for (auto l = logs.begin(); l != logs.end(); ++l) {
+ MOZ_MTLOG(ML_INFO, *l);
+ }
+ }
+
+ SignalConnectionStateChange(this, state);
+}
+
+void NrIceCtx::SetGatheringState(GatheringState state) {
+ if (state == gathering_state_)
+ return;
+
+ MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << name_ << "): gathering state " <<
+ gathering_state_ << "->" << state);
+ gathering_state_ = state;
+
+ SignalGatheringStateChange(this, state);
+}
+
+} // close namespace
+
+// Reimplement nr_ice_compute_codeword to avoid copyright issues
+void nr_ice_compute_codeword(char *buf, int len,char *codeword) {
+ UINT4 c;
+
+ r_crc32(buf,len,&c);
+
+ PL_Base64Encode(reinterpret_cast<char*>(&c), 3, codeword);
+ codeword[4] = 0;
+
+ return;
+}
diff --git a/media/mtransport/nricectx.h b/media/mtransport/nricectx.h
new file mode 100644
index 000000000..e6a8a34b5
--- /dev/null
+++ b/media/mtransport/nricectx.h
@@ -0,0 +1,390 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+// This is a wrapper around the nICEr ICE stack
+#ifndef nricectx_h__
+#define nricectx_h__
+
+#include <string>
+#include <vector>
+
+#include "sigslot.h"
+
+#include "prnetdb.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "nsAutoPtr.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+
+#include "m_cpp_utils.h"
+
+typedef struct nr_ice_ctx_ nr_ice_ctx;
+typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx;
+typedef struct nr_ice_media_stream_ nr_ice_media_stream;
+typedef struct nr_ice_handler_ nr_ice_handler;
+typedef struct nr_ice_handler_vtbl_ nr_ice_handler_vtbl;
+typedef struct nr_ice_candidate_ nr_ice_candidate;
+typedef struct nr_ice_cand_pair_ nr_ice_cand_pair;
+typedef struct nr_ice_stun_server_ nr_ice_stun_server;
+typedef struct nr_ice_turn_server_ nr_ice_turn_server;
+typedef struct nr_resolver_ nr_resolver;
+typedef struct nr_proxy_tunnel_config_ nr_proxy_tunnel_config;
+
+typedef void* NR_SOCKET;
+
+namespace mozilla {
+
+// Timestamps set whenever a packet is dropped due to global rate limiting
+// (see nr_socket_prsock.cpp)
+TimeStamp nr_socket_short_term_violation_time();
+TimeStamp nr_socket_long_term_violation_time();
+
+class NrIceMediaStream;
+
+extern const char kNrIceTransportUdp[];
+extern const char kNrIceTransportTcp[];
+
+class NrIceStunServer {
+ public:
+ explicit NrIceStunServer(const PRNetAddr& addr) : has_addr_(true) {
+ memcpy(&addr_, &addr, sizeof(addr));
+ }
+
+ // The main function to use. Will take either an address or a hostname.
+ static UniquePtr<NrIceStunServer> Create(const std::string& addr, uint16_t port,
+ const char *transport = kNrIceTransportUdp) {
+ UniquePtr<NrIceStunServer> server(new NrIceStunServer(transport));
+
+ nsresult rv = server->Init(addr, port);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ return server;
+ }
+
+ nsresult ToNicerStunStruct(nr_ice_stun_server* server) const;
+
+ protected:
+ explicit NrIceStunServer(const char *transport) :
+ addr_(), transport_(transport) {}
+
+ nsresult Init(const std::string& addr, uint16_t port) {
+ PRStatus status = PR_StringToNetAddr(addr.c_str(), &addr_);
+ if (status == PR_SUCCESS) {
+ // Parseable as an address
+ addr_.inet.port = PR_htons(port);
+ port_ = port;
+ has_addr_ = true;
+ return NS_OK;
+ }
+ else if (addr.size() < 256) {
+ // Apparently this is a hostname.
+ host_ = addr;
+ port_ = port;
+ has_addr_ = false;
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+ }
+
+ bool has_addr_;
+ std::string host_;
+ uint16_t port_;
+ PRNetAddr addr_;
+ std::string transport_;
+};
+
+class NrIceTurnServer : public NrIceStunServer {
+ public:
+ static UniquePtr<NrIceTurnServer> Create(const std::string& addr, uint16_t port,
+ const std::string& username,
+ const std::vector<unsigned char>& password,
+ const char *transport = kNrIceTransportUdp) {
+ UniquePtr<NrIceTurnServer> server(new NrIceTurnServer(username, password, transport));
+
+ nsresult rv = server->Init(addr, port);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ return server;
+ }
+
+ nsresult ToNicerTurnStruct(nr_ice_turn_server *server) const;
+
+ private:
+ NrIceTurnServer(const std::string& username,
+ const std::vector<unsigned char>& password,
+ const char *transport) :
+ NrIceStunServer(transport), username_(username), password_(password) {}
+
+ std::string username_;
+ std::vector<unsigned char> password_;
+};
+
+class NrIceProxyServer {
+ public:
+ NrIceProxyServer(const std::string& host, uint16_t port,
+ const std::string& alpn) :
+ host_(host), port_(port), alpn_(alpn) {
+ }
+
+ NrIceProxyServer() : NrIceProxyServer("", 0, "") {}
+
+ const std::string& host() const { return host_; }
+ uint16_t port() const { return port_; }
+ const std::string& alpn() const { return alpn_; }
+
+ private:
+ std::string host_;
+ uint16_t port_;
+ std::string alpn_;
+};
+
+class TestNat;
+
+class NrIceCtx {
+ friend class NrIceCtxHandler;
+ public:
+ enum ConnectionState { ICE_CTX_INIT,
+ ICE_CTX_CHECKING,
+ ICE_CTX_CONNECTED,
+ ICE_CTX_COMPLETED,
+ ICE_CTX_FAILED,
+ ICE_CTX_DISCONNECTED,
+ ICE_CTX_CLOSED
+ };
+
+ enum GatheringState { ICE_CTX_GATHER_INIT,
+ ICE_CTX_GATHER_STARTED,
+ ICE_CTX_GATHER_COMPLETE
+ };
+
+ enum Controlling { ICE_CONTROLLING,
+ ICE_CONTROLLED
+ };
+
+ enum Policy { ICE_POLICY_RELAY,
+ ICE_POLICY_NO_HOST,
+ ICE_POLICY_ALL
+ };
+
+ // initialize ICE globals, crypto, and logging
+ static void InitializeGlobals(bool allow_loopback = false,
+ bool tcp_enabled = true,
+ bool allow_link_local = false);
+ static std::string GetNewUfrag();
+ static std::string GetNewPwd();
+
+ bool Initialize();
+ bool Initialize(const std::string& ufrag, const std::string& pwd);
+
+ int SetNat(const RefPtr<TestNat>& aNat);
+
+ // Deinitialize all ICE global state. Used only for testing.
+ static void internal_DeinitializeGlobal();
+
+ // Divide some timers to faster testing. Used only for testing.
+ void internal_SetTimerAccelarator(int divider);
+
+ nr_ice_ctx *ctx() { return ctx_; }
+ nr_ice_peer_ctx *peer() { return peer_; }
+
+ // Testing only.
+ void destroy_peer_ctx();
+
+ void SetStream(size_t index, NrIceMediaStream* stream);
+
+ RefPtr<NrIceMediaStream> GetStream(size_t index) {
+ if (index < streams_.size()) {
+ return streams_[index];
+ }
+ return nullptr;
+ }
+
+ // Some might be null
+ size_t GetStreamCount() const
+ {
+ return streams_.size();
+ }
+
+ // The name of the ctx
+ const std::string& name() const { return name_; }
+
+ // Get ufrag and password.
+ std::string ufrag() const;
+ std::string pwd() const;
+
+ // Current state
+ ConnectionState connection_state() const {
+ return connection_state_;
+ }
+
+ // Current state
+ GatheringState gathering_state() const {
+ return gathering_state_;
+ }
+
+ // Get the global attributes
+ std::vector<std::string> GetGlobalAttributes();
+
+ // Set the other side's global attributes
+ nsresult ParseGlobalAttributes(std::vector<std::string> attrs);
+
+ // Set whether we are controlling or not.
+ nsresult SetControlling(Controlling controlling);
+
+ Controlling GetControlling();
+
+ // Set whether we're allowed to produce none, relay or all candidates.
+ // TODO(jib@mozilla.com): Work out what change means mid-connection (1181768)
+ nsresult SetPolicy(Policy policy);
+
+ Policy policy() const {
+ return policy_;
+ }
+
+ // Set the STUN servers. Must be called before StartGathering
+ // (if at all).
+ nsresult SetStunServers(const std::vector<NrIceStunServer>& stun_servers);
+
+ // Set the TURN servers. Must be called before StartGathering
+ // (if at all).
+ nsresult SetTurnServers(const std::vector<NrIceTurnServer>& turn_servers);
+
+ // Provide the resolution provider. Must be called before
+ // StartGathering.
+ nsresult SetResolver(nr_resolver *resolver);
+
+ // Provide the proxy address. Must be called before
+ // StartGathering.
+ nsresult SetProxyServer(const NrIceProxyServer& proxy_server);
+
+ // Start ICE gathering
+ nsresult StartGathering(bool default_route_only, bool proxy_only);
+
+ // Start checking
+ nsresult StartChecks();
+
+ // Finalize the ICE negotiation. I.e., there will be no
+ // more forking.
+ nsresult Finalize();
+
+ // Are we trickling?
+ bool generating_trickle() const { return trickle_; }
+
+ // Signals to indicate events. API users can (and should)
+ // register for these.
+ sigslot::signal2<NrIceCtx*, NrIceCtx::GatheringState>
+ SignalGatheringStateChange;
+ sigslot::signal2<NrIceCtx*, NrIceCtx::ConnectionState>
+ SignalConnectionStateChange;
+
+ // The thread to direct method calls to
+ nsCOMPtr<nsIEventTarget> thread() { return sts_target_; }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtx)
+
+private:
+ NrIceCtx(const std::string& name,
+ bool offerer,
+ Policy policy);
+
+ virtual ~NrIceCtx();
+
+ DISALLOW_COPY_ASSIGN(NrIceCtx);
+
+ // Callbacks for nICEr
+ static void gather_cb(NR_SOCKET s, int h, void *arg); // ICE gather complete
+
+ // Handler implementation
+ static int select_pair(void *obj,nr_ice_media_stream *stream,
+ int component_id, nr_ice_cand_pair **potentials,
+ int potential_ct);
+ static int stream_ready(void *obj, nr_ice_media_stream *stream);
+ static int stream_failed(void *obj, nr_ice_media_stream *stream);
+ static int ice_checking(void *obj, nr_ice_peer_ctx *pctx);
+ static int ice_connected(void *obj, nr_ice_peer_ctx *pctx);
+ static int ice_disconnected(void *obj, nr_ice_peer_ctx *pctx);
+ static int msg_recvd(void *obj, nr_ice_peer_ctx *pctx,
+ nr_ice_media_stream *stream, int component_id,
+ unsigned char *msg, int len);
+ static void trickle_cb(void *arg, nr_ice_ctx *ctx, nr_ice_media_stream *stream,
+ int component_id, nr_ice_candidate *candidate);
+
+ // Find a media stream by stream ptr. Gross
+ RefPtr<NrIceMediaStream> FindStream(nr_ice_media_stream *stream);
+
+ // Set the state
+ void SetConnectionState(ConnectionState state);
+
+ // Set the state
+ void SetGatheringState(GatheringState state);
+
+ ConnectionState connection_state_;
+ GatheringState gathering_state_;
+ const std::string name_;
+ bool offerer_;
+ bool ice_controlling_set_;
+ std::vector<RefPtr<NrIceMediaStream> > streams_;
+ nr_ice_ctx *ctx_;
+ nr_ice_peer_ctx *peer_;
+ nr_ice_handler_vtbl* ice_handler_vtbl_; // Must be pointer
+ nr_ice_handler* ice_handler_; // Must be pointer
+ bool trickle_;
+ nsCOMPtr<nsIEventTarget> sts_target_; // The thread to run on
+ Policy policy_;
+ RefPtr<TestNat> nat_;
+};
+
+
+} // close namespace
+#endif
diff --git a/media/mtransport/nricectxhandler.cpp b/media/mtransport/nricectxhandler.cpp
new file mode 100644
index 000000000..4cea6c2c1
--- /dev/null
+++ b/media/mtransport/nricectxhandler.cpp
@@ -0,0 +1,160 @@
+#include <sstream>
+
+#include "logging.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "ice_ctx.h"
+}
+
+// Local includes
+#include "nricectxhandler.h"
+#include "nricemediastream.h"
+#include "nriceresolver.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+NrIceCtxHandler::NrIceCtxHandler(const std::string& name,
+ bool offerer,
+ NrIceCtx::Policy policy)
+ : current_ctx(new NrIceCtx(name, offerer, policy)),
+ old_ctx(nullptr),
+ restart_count(0)
+{
+}
+
+RefPtr<NrIceCtxHandler>
+NrIceCtxHandler::Create(const std::string& name,
+ bool offerer,
+ bool allow_loopback,
+ bool tcp_enabled,
+ bool allow_link_local,
+ NrIceCtx::Policy policy)
+{
+ // InitializeGlobals only executes once
+ NrIceCtx::InitializeGlobals(allow_loopback, tcp_enabled, allow_link_local);
+
+ RefPtr<NrIceCtxHandler> ctx = new NrIceCtxHandler(name, offerer, policy);
+
+ if (ctx == nullptr ||
+ ctx->current_ctx == nullptr ||
+ !ctx->current_ctx->Initialize()) {
+ return nullptr;
+ }
+
+ return ctx;
+}
+
+
+RefPtr<NrIceMediaStream>
+NrIceCtxHandler::CreateStream(const std::string& name, int components)
+{
+ // To make tracking NrIceMediaStreams easier during ICE restart
+ // prepend an int to the name that increments with each ICE restart
+ std::ostringstream os;
+ os << restart_count << "-" << name;
+ return NrIceMediaStream::Create(this->current_ctx, os.str(), components);
+}
+
+
+RefPtr<NrIceCtx>
+NrIceCtxHandler::CreateCtx() const
+{
+ return CreateCtx(NrIceCtx::GetNewUfrag(), NrIceCtx::GetNewPwd());
+}
+
+
+RefPtr<NrIceCtx>
+NrIceCtxHandler::CreateCtx(const std::string& ufrag,
+ const std::string& pwd) const
+{
+ RefPtr<NrIceCtx> new_ctx = new NrIceCtx(this->current_ctx->name(),
+ true, // offerer (hardcoded per bwc)
+ this->current_ctx->policy());
+ if (new_ctx == nullptr) {
+ return nullptr;
+ }
+
+ if (!new_ctx->Initialize(ufrag, pwd)) {
+ return nullptr;
+ }
+
+ // copy the stun, and turn servers from the current context
+ int r = nr_ice_ctx_set_stun_servers(new_ctx->ctx_,
+ this->current_ctx->ctx_->stun_servers,
+ this->current_ctx->ctx_->stun_server_ct);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Error while setting STUN servers in CreateCtx"
+ << " (likely ice restart related)");
+ return nullptr;
+ }
+
+ r = nr_ice_ctx_copy_turn_servers(new_ctx->ctx_,
+ this->current_ctx->ctx_->turn_servers,
+ this->current_ctx->ctx_->turn_server_ct);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Error while copying TURN servers in CreateCtx"
+ << " (likely ice restart related)");
+ return nullptr;
+ }
+
+ // grab the NrIceResolver stashed in the nr_resolver and allocate another
+ // for the new ctx. Note: there may not be an nr_resolver.
+ if (this->current_ctx->ctx_->resolver) {
+ NrIceResolver* resolver =
+ static_cast<NrIceResolver*>(this->current_ctx->ctx_->resolver->obj);
+ if (!resolver ||
+ NS_FAILED(new_ctx->SetResolver(resolver->AllocateResolver()))) {
+ MOZ_MTLOG(ML_ERROR, "Error while setting dns resolver in CreateCtx"
+ << " (likely ice restart related)");
+ return nullptr;
+ }
+ }
+
+ return new_ctx;
+}
+
+
+bool
+NrIceCtxHandler::BeginIceRestart(RefPtr<NrIceCtx> new_ctx)
+{
+ MOZ_ASSERT(!old_ctx, "existing ice restart in progress");
+ if (old_ctx) {
+ MOZ_MTLOG(ML_ERROR, "Existing ice restart in progress");
+ return false; // ice restart already in progress
+ }
+
+ if (new_ctx == nullptr) {
+ return false;
+ }
+
+ ++restart_count;
+ old_ctx = current_ctx;
+ current_ctx = new_ctx;
+ return true;
+}
+
+
+void
+NrIceCtxHandler::FinalizeIceRestart()
+{
+ // no harm calling this even if we're not in the middle of restarting
+ old_ctx = nullptr;
+}
+
+
+void
+NrIceCtxHandler::RollbackIceRestart()
+{
+ if (old_ctx == nullptr) {
+ return;
+ }
+ current_ctx = old_ctx;
+ old_ctx = nullptr;
+}
+
+
+} // close namespace
diff --git a/media/mtransport/nricectxhandler.h b/media/mtransport/nricectxhandler.h
new file mode 100644
index 000000000..492a19ccf
--- /dev/null
+++ b/media/mtransport/nricectxhandler.h
@@ -0,0 +1,52 @@
+#ifndef nricectxhandler_h__
+#define nricectxhandler_h__
+
+#include "nricectx.h"
+
+namespace mozilla {
+
+class NrIceCtxHandler {
+public:
+ // TODO(ekr@rtfm.com): Too many bools here. Bug 1193437.
+ static RefPtr<NrIceCtxHandler> Create(const std::string& name,
+ bool offerer,
+ bool allow_loopback = false,
+ bool tcp_enabled = true,
+ bool allow_link_local = false,
+ NrIceCtx::Policy policy =
+ NrIceCtx::ICE_POLICY_ALL);
+
+ RefPtr<NrIceMediaStream> CreateStream(const std::string& name,
+ int components);
+ // CreateCtx is necessary so we can create and initialize the context
+ // on main thread, but begin the ice restart mechanics on STS thread
+ RefPtr<NrIceCtx> CreateCtx() const; // for test
+ RefPtr<NrIceCtx> CreateCtx(const std::string& ufrag,
+ const std::string& pwd) const;
+
+ RefPtr<NrIceCtx> ctx() { return current_ctx; }
+
+ bool BeginIceRestart(RefPtr<NrIceCtx> new_ctx);
+ bool IsRestarting() const { return (old_ctx != nullptr); }
+ void FinalizeIceRestart();
+ void RollbackIceRestart();
+
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtxHandler)
+
+private:
+ NrIceCtxHandler(const std::string& name,
+ bool offerer,
+ NrIceCtx::Policy policy);
+ NrIceCtxHandler() = delete;
+ ~NrIceCtxHandler() {}
+ DISALLOW_COPY_ASSIGN(NrIceCtxHandler);
+
+ RefPtr<NrIceCtx> current_ctx;
+ RefPtr<NrIceCtx> old_ctx; // for while restart is in progress
+ int restart_count; // used to differentiate streams between restarted ctx
+};
+
+} // close namespace
+
+#endif // nricectxhandler_h__
diff --git a/media/mtransport/nricemediastream.cpp b/media/mtransport/nricemediastream.cpp
new file mode 100644
index 000000000..1516d416e
--- /dev/null
+++ b/media/mtransport/nricemediastream.cpp
@@ -0,0 +1,610 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <string>
+#include <vector>
+
+#include "logging.h"
+#include "nsError.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "registry.h"
+#include "async_timer.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_handler.h"
+}
+
+// Local includes
+#include "nricectx.h"
+#include "nricemediastream.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+static bool ToNrIceAddr(nr_transport_addr &addr,
+ NrIceAddr *out) {
+ int r;
+ char addrstring[INET6_ADDRSTRLEN + 1];
+
+ r = nr_transport_addr_get_addrstring(&addr, addrstring, sizeof(addrstring));
+ if (r)
+ return false;
+ out->host = addrstring;
+
+ int port;
+ r = nr_transport_addr_get_port(&addr, &port);
+ if (r)
+ return false;
+
+ out->port = port;
+
+ switch (addr.protocol) {
+ case IPPROTO_TCP:
+ out->transport = kNrIceTransportTcp;
+ break;
+ case IPPROTO_UDP:
+ out->transport = kNrIceTransportUdp;
+ break;
+ default:
+ MOZ_CRASH();
+ return false;
+ }
+
+ return true;
+}
+
+static bool ToNrIceCandidate(const nr_ice_candidate& candc,
+ NrIceCandidate* out) {
+ MOZ_ASSERT(out);
+ int r;
+ // Const-cast because the internal nICEr code isn't const-correct.
+ nr_ice_candidate *cand = const_cast<nr_ice_candidate *>(&candc);
+
+ if (!ToNrIceAddr(cand->addr, &out->cand_addr))
+ return false;
+
+ if (cand->isock) {
+ nr_transport_addr addr;
+ r = nr_socket_getaddr(cand->isock->sock, &addr);
+ if (r)
+ return false;
+
+ if (!ToNrIceAddr(addr, &out->local_addr))
+ return false;
+ }
+
+ NrIceCandidate::Type type;
+
+ switch (cand->type) {
+ case HOST:
+ type = NrIceCandidate::ICE_HOST;
+ break;
+ case SERVER_REFLEXIVE:
+ type = NrIceCandidate::ICE_SERVER_REFLEXIVE;
+ break;
+ case PEER_REFLEXIVE:
+ type = NrIceCandidate::ICE_PEER_REFLEXIVE;
+ break;
+ case RELAYED:
+ type = NrIceCandidate::ICE_RELAYED;
+ break;
+ default:
+ return false;
+ }
+
+ NrIceCandidate::TcpType tcp_type;
+ switch (cand->tcp_type) {
+ case TCP_TYPE_ACTIVE:
+ tcp_type = NrIceCandidate::ICE_ACTIVE;
+ break;
+ case TCP_TYPE_PASSIVE:
+ tcp_type = NrIceCandidate::ICE_PASSIVE;
+ break;
+ case TCP_TYPE_SO:
+ tcp_type = NrIceCandidate::ICE_SO;
+ break;
+ default:
+ tcp_type = NrIceCandidate::ICE_NONE;
+ break;
+ }
+
+ out->type = type;
+ out->tcp_type = tcp_type;
+ out->codeword = candc.codeword;
+ return true;
+}
+
+// Make an NrIceCandidate from the candidate |cand|.
+// This is not a member fxn because we want to hide the
+// defn of nr_ice_candidate but we pass by reference.
+static UniquePtr<NrIceCandidate> MakeNrIceCandidate(const nr_ice_candidate& candc) {
+ UniquePtr<NrIceCandidate> out(new NrIceCandidate());
+
+ if (!ToNrIceCandidate(candc, out.get())) {
+ return nullptr;
+ }
+ return out;
+}
+
+// NrIceMediaStream
+RefPtr<NrIceMediaStream>
+NrIceMediaStream::Create(NrIceCtx *ctx,
+ const std::string& name,
+ int components) {
+ RefPtr<NrIceMediaStream> stream =
+ new NrIceMediaStream(ctx, name, components);
+ MOZ_ASSERT(stream->ctx_ == ctx->ctx());
+
+ int r = nr_ice_add_media_stream(ctx->ctx(),
+ const_cast<char *>(name.c_str()),
+ components, &stream->stream_);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '"
+ << name << "'");
+ return nullptr;
+ }
+
+ return stream;
+}
+
+NrIceMediaStream::NrIceMediaStream(NrIceCtx *ctx,
+ const std::string& name,
+ size_t components) :
+ state_(ICE_CONNECTING),
+ ctx_(ctx->ctx()),
+ ctx_peer_(ctx->peer()),
+ name_(name),
+ components_(components),
+ stream_(nullptr),
+ level_(0),
+ has_parsed_attrs_(false)
+{
+}
+
+NrIceMediaStream::~NrIceMediaStream() {
+ // We do not need to destroy anything. All major resources
+ // are attached to the ice ctx.
+}
+
+nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
+ attributes) {
+ if (!stream_)
+ return NS_ERROR_FAILURE;
+
+ std::vector<char *> attributes_in;
+
+ for (size_t i=0; i<attributes.size(); ++i) {
+ attributes_in.push_back(const_cast<char *>(attributes[i].c_str()));
+ }
+
+ // Still need to call nr_ice_ctx_parse_stream_attributes.
+ int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_peer_,
+ stream_,
+ attributes_in.size() ?
+ &attributes_in[0] : nullptr,
+ attributes_in.size());
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't parse attributes for stream "
+ << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ has_parsed_attrs_ = true;
+ return NS_OK;
+}
+
+// Parse trickle ICE candidate
+nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate) {
+ int r;
+
+ MOZ_MTLOG(ML_DEBUG, "NrIceCtx(" << ctx_->label << ")/STREAM(" <<
+ name() << ") : parsing trickle candidate " << candidate);
+
+ r = nr_ice_peer_ctx_parse_trickle_candidate(ctx_peer_,
+ stream_,
+ const_cast<char *>(
+ candidate.c_str())
+ );
+ if (r) {
+ if (r == R_ALREADY) {
+ MOZ_MTLOG(ML_ERROR, "Trickle candidates are redundant for stream '"
+ << name_ << "' because it is completed");
+
+ } else {
+ MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
+ << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+// Returns NS_ERROR_NOT_AVAILABLE if component is unpaired or disabled.
+nsresult NrIceMediaStream::GetActivePair(int component,
+ UniquePtr<NrIceCandidate>* localp,
+ UniquePtr<NrIceCandidate>* remotep) {
+ int r;
+ nr_ice_candidate *local_int;
+ nr_ice_candidate *remote_int;
+
+ if (!stream_) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ r = nr_ice_media_stream_get_active(ctx_peer_,
+ stream_,
+ component,
+ &local_int, &remote_int);
+ // If result is R_REJECTED then component is unpaired or disabled.
+ if (r == R_REJECTED)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ if (r)
+ return NS_ERROR_FAILURE;
+
+ UniquePtr<NrIceCandidate> local(
+ MakeNrIceCandidate(*local_int));
+ if (!local)
+ return NS_ERROR_FAILURE;
+
+ UniquePtr<NrIceCandidate> remote(
+ MakeNrIceCandidate(*remote_int));
+ if (!remote)
+ return NS_ERROR_FAILURE;
+
+ if (localp)
+ *localp = Move(local);
+ if (remotep)
+ *remotep = Move(remote);
+
+ return NS_OK;
+}
+
+
+nsresult NrIceMediaStream::GetCandidatePairs(std::vector<NrIceCandidatePair>*
+ out_pairs) const {
+ MOZ_ASSERT(out_pairs);
+ if (!stream_) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // If we haven't at least started checking then there is nothing to report
+ if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
+ return NS_OK;
+ }
+
+ // Get the check_list on the peer stream (this is where the check_list
+ // actually lives, not in stream_)
+ nr_ice_media_stream* peer_stream;
+ int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
+ if (r != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nr_ice_cand_pair *p1, *p2;
+ out_pairs->clear();
+
+ TAILQ_FOREACH(p1, &peer_stream->check_list, check_queue_entry) {
+ MOZ_ASSERT(p1);
+ MOZ_ASSERT(p1->local);
+ MOZ_ASSERT(p1->remote);
+ NrIceCandidatePair pair;
+
+ p2 = TAILQ_FIRST(&peer_stream->check_list);
+ while (p2) {
+ if (p1 == p2) {
+ /* Don't compare with our self. */
+ p2=TAILQ_NEXT(p2, check_queue_entry);
+ continue;
+ }
+ if (strncmp(p1->codeword,p2->codeword,sizeof(p1->codeword))==0) {
+ /* In case of duplicate pairs we only report the one winning pair */
+ if (
+ ((p2->remote->component && (p2->remote->component->active == p2)) &&
+ !(p1->remote->component && (p1->remote->component->active == p1))) ||
+ ((p2->peer_nominated || p2->nominated) &&
+ !(p1->peer_nominated || p1->nominated)) ||
+ (p2->priority > p1->priority) ||
+ ((p2->state == NR_ICE_PAIR_STATE_SUCCEEDED) &&
+ (p1->state != NR_ICE_PAIR_STATE_SUCCEEDED)) ||
+ ((p2->state != NR_ICE_PAIR_STATE_CANCELLED) &&
+ (p1->state == NR_ICE_PAIR_STATE_CANCELLED))
+ ) {
+ /* p2 is a better pair. */
+ break;
+ }
+ }
+ p2=TAILQ_NEXT(p2, check_queue_entry);
+ }
+ if (p2) {
+ /* p2 points to a duplicate but better pair so skip this one */
+ continue;
+ }
+
+ switch (p1->state) {
+ case NR_ICE_PAIR_STATE_FROZEN:
+ pair.state = NrIceCandidatePair::State::STATE_FROZEN;
+ break;
+ case NR_ICE_PAIR_STATE_WAITING:
+ pair.state = NrIceCandidatePair::State::STATE_WAITING;
+ break;
+ case NR_ICE_PAIR_STATE_IN_PROGRESS:
+ pair.state = NrIceCandidatePair::State::STATE_IN_PROGRESS;
+ break;
+ case NR_ICE_PAIR_STATE_FAILED:
+ pair.state = NrIceCandidatePair::State::STATE_FAILED;
+ break;
+ case NR_ICE_PAIR_STATE_SUCCEEDED:
+ pair.state = NrIceCandidatePair::State::STATE_SUCCEEDED;
+ break;
+ case NR_ICE_PAIR_STATE_CANCELLED:
+ pair.state = NrIceCandidatePair::State::STATE_CANCELLED;
+ break;
+ default:
+ MOZ_ASSERT(0);
+ }
+
+ pair.priority = p1->priority;
+ pair.nominated = p1->peer_nominated || p1->nominated;
+ pair.selected = p1->remote->component &&
+ p1->remote->component->active == p1;
+ pair.codeword = p1->codeword;
+
+ if (!ToNrIceCandidate(*(p1->local), &pair.local) ||
+ !ToNrIceCandidate(*(p1->remote), &pair.remote)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ out_pairs->push_back(pair);
+ }
+
+ return NS_OK;
+}
+
+nsresult NrIceMediaStream::GetDefaultCandidate(
+ int component,
+ NrIceCandidate* candidate) const {
+
+ nr_ice_candidate *cand;
+
+ int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
+ << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!ToNrIceCandidate(*cand, candidate)) {
+ MOZ_MTLOG(ML_ERROR, "Failed to convert default ICE candidate for '"
+ << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+std::vector<std::string> NrIceMediaStream::GetCandidates() const {
+ char **attrs = 0;
+ int attrct;
+ int r;
+ std::vector<std::string> ret;
+
+ if (!stream_) {
+ return ret;
+ }
+
+ r = nr_ice_media_stream_get_attributes(stream_,
+ &attrs, &attrct);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get ICE candidates for '"
+ << name_ << "'");
+ return ret;
+ }
+
+ for (int i=0; i<attrct; i++) {
+ ret.push_back(attrs[i]);
+ RFREE(attrs[i]);
+ }
+
+ RFREE(attrs);
+
+ return ret;
+}
+
+static nsresult GetCandidatesFromStream(
+ nr_ice_media_stream *stream,
+ std::vector<NrIceCandidate> *candidates) {
+ MOZ_ASSERT(candidates);
+ nr_ice_component* comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if (comp->state != NR_ICE_COMPONENT_DISABLED) {
+ nr_ice_candidate *cand = TAILQ_FIRST(&comp->candidates);
+ while(cand){
+ NrIceCandidate new_cand;
+ // This can fail if the candidate is server reflexive or relayed, and
+ // has not yet received a response (ie; it doesn't know its address
+ // yet). For the purposes of this code, this isn't a candidate we're
+ // interested in, since it is not fully baked yet.
+ if (ToNrIceCandidate(*cand, &new_cand)) {
+ candidates->push_back(new_cand);
+ }
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+ }
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ return NS_OK;
+}
+
+nsresult NrIceMediaStream::GetLocalCandidates(
+ std::vector<NrIceCandidate>* candidates) const {
+ if (!stream_) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return GetCandidatesFromStream(stream_, candidates);
+}
+
+nsresult NrIceMediaStream::GetRemoteCandidates(
+ std::vector<NrIceCandidate>* candidates) const {
+ if (!stream_) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // If we haven't at least started checking then there is nothing to report
+ if (ctx_peer_->state != NR_ICE_PEER_STATE_PAIRED) {
+ return NS_OK;
+ }
+
+ nr_ice_media_stream* peer_stream;
+ int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
+ if (r != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return GetCandidatesFromStream(peer_stream, candidates);
+}
+
+
+nsresult NrIceMediaStream::DisableComponent(int component_id) {
+ if (!stream_)
+ return NS_ERROR_FAILURE;
+
+ int r = nr_ice_media_stream_disable_component(stream_,
+ component_id);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable '" << name_ << "':" <<
+ component_id);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult NrIceMediaStream::GetConsentStatus(int component_id, bool *can_send, struct timeval *ts) {
+ if (!stream_)
+ return NS_ERROR_FAILURE;
+
+ nr_ice_media_stream* peer_stream;
+ int r = nr_ice_peer_ctx_find_pstream(ctx_peer_, stream_, &peer_stream);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Failed to find peer stream for '" << name_ << "':" <<
+ component_id);
+ return NS_ERROR_FAILURE;
+ }
+
+ int send = 0;
+ r = nr_ice_media_stream_get_consent_status(peer_stream, component_id,
+ &send, ts);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Failed to get consent status for '" << name_ << "':" <<
+ component_id);
+ return NS_ERROR_FAILURE;
+ }
+ *can_send = !!send;
+
+ return NS_OK;
+}
+
+nsresult NrIceMediaStream::SendPacket(int component_id,
+ const unsigned char *data,
+ size_t len) {
+ if (!stream_)
+ return NS_ERROR_FAILURE;
+
+ int r = nr_ice_media_stream_send(ctx_peer_, stream_,
+ component_id,
+ const_cast<unsigned char *>(data), len);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't send media on '" << name_ << "'");
+ if (r == R_WOULDBLOCK) {
+ return NS_BASE_STREAM_WOULD_BLOCK;
+ }
+
+ return NS_BASE_STREAM_OSERROR;
+ }
+
+ return NS_OK;
+}
+
+
+void NrIceMediaStream::Ready() {
+ // This function is called whenever a stream becomes ready, but it
+ // gets fired multiple times when a stream gets nominated repeatedly.
+ if (state_ != ICE_OPEN) {
+ MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'");
+ state_ = ICE_OPEN;
+ SignalReady(this);
+ }
+ else {
+ MOZ_MTLOG(ML_DEBUG, "Stream ready callback fired again for '" << name_ << "'");
+ }
+}
+
+void NrIceMediaStream::Close() {
+ MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
+ state_ = ICE_CLOSED;
+
+ if (stream_) {
+ int r = nr_ice_remove_media_stream(ctx_, &stream_);
+ if (r) {
+ MOZ_ASSERT(false, "Failed to remove stream");
+ MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r);
+ }
+ }
+}
+
+} // close namespace
diff --git a/media/mtransport/nricemediastream.h b/media/mtransport/nricemediastream.h
new file mode 100644
index 000000000..9b3b470ff
--- /dev/null
+++ b/media/mtransport/nricemediastream.h
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// This is a wrapper around the nICEr ICE stack
+#ifndef nricemediastream_h__
+#define nricemediastream_h__
+
+#include <string>
+#include <vector>
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+
+#include "m_cpp_utils.h"
+
+
+namespace mozilla {
+
+typedef struct nr_ice_ctx_ nr_ice_ctx;
+typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx;
+typedef struct nr_ice_media_stream_ nr_ice_media_stream;
+
+class NrIceCtx;
+
+struct NrIceAddr {
+ std::string host;
+ uint16_t port;
+ std::string transport;
+};
+
+/* A summary of a candidate, for use in asking which candidate
+ pair is active */
+struct NrIceCandidate {
+ enum Type {
+ ICE_HOST,
+ ICE_SERVER_REFLEXIVE,
+ ICE_PEER_REFLEXIVE,
+ ICE_RELAYED
+ };
+
+ enum TcpType {
+ ICE_NONE,
+ ICE_ACTIVE,
+ ICE_PASSIVE,
+ ICE_SO
+ };
+
+ NrIceAddr cand_addr;
+ NrIceAddr local_addr;
+ Type type;
+ TcpType tcp_type;
+ std::string codeword;
+};
+
+struct NrIceCandidatePair {
+
+ enum State {
+ STATE_FROZEN,
+ STATE_WAITING,
+ STATE_IN_PROGRESS,
+ STATE_FAILED,
+ STATE_SUCCEEDED,
+ STATE_CANCELLED
+ };
+
+ State state;
+ uint64_t priority;
+ // Set regardless of who nominated it. Does not necessarily mean that it is
+ // ready to be selected (ie; nominated by peer, but our check has not
+ // succeeded yet.) Note: since this implementation uses aggressive nomination,
+ // when we are the controlling agent, this will always be set if the pair is
+ // in STATE_SUCCEEDED.
+ bool nominated;
+ // Set if this candidate pair has been selected. Note: Since we are using
+ // aggressive nomination, this could change frequently as ICE runs.
+ bool selected;
+ NrIceCandidate local;
+ NrIceCandidate remote;
+ // TODO(bcampen@mozilla.com): Is it important to put the foundation in here?
+ std::string codeword;
+};
+
+class NrIceMediaStream {
+ public:
+ static RefPtr<NrIceMediaStream> Create(NrIceCtx *ctx,
+ const std::string& name,
+ int components);
+ enum State { ICE_CONNECTING, ICE_OPEN, ICE_CLOSED};
+
+ State state() const { return state_; }
+
+ // The name of the stream
+ const std::string& name() const { return name_; }
+
+ // Get all the candidates
+ std::vector<std::string> GetCandidates() const;
+
+ nsresult GetLocalCandidates(std::vector<NrIceCandidate>* candidates) const;
+ nsresult GetRemoteCandidates(std::vector<NrIceCandidate>* candidates) const;
+
+ // Get all candidate pairs, whether in the check list or triggered check
+ // queue, in priority order. |out_pairs| is cleared before being filled.
+ nsresult GetCandidatePairs(std::vector<NrIceCandidatePair>* out_pairs) const;
+
+ nsresult GetDefaultCandidate(int component, NrIceCandidate* candidate) const;
+
+ // Parse remote attributes
+ nsresult ParseAttributes(std::vector<std::string>& candidates);
+ bool HasParsedAttributes() const { return has_parsed_attrs_; }
+
+ // Parse trickle ICE candidate
+ nsresult ParseTrickleCandidate(const std::string& candidate);
+
+ // Disable a component
+ nsresult DisableComponent(int component);
+
+ // Get the candidate pair currently active. It's the
+ // caller's responsibility to free these.
+ nsresult GetActivePair(int component,
+ UniquePtr<NrIceCandidate>* local,
+ UniquePtr<NrIceCandidate>* remote);
+
+ // Get the current ICE consent send status plus the timeval of the last
+ // consent update time.
+ nsresult GetConsentStatus(int component, bool *can_send, struct timeval *ts);
+
+ // The number of components
+ size_t components() const { return components_; }
+
+ // The underlying nICEr stream
+ nr_ice_media_stream *stream() { return stream_; }
+ // Signals to indicate events. API users can (and should)
+ // register for these.
+
+ // Send a packet
+ nsresult SendPacket(int component_id, const unsigned char *data, size_t len);
+
+ // Set your state to ready. Called by the NrIceCtx;
+ void Ready();
+
+ // Close the stream. Called by the NrIceCtx.
+ // Different from the destructor because other people
+ // might be holding RefPtrs but we want those writes to fail once
+ // the context has been destroyed.
+ void Close();
+
+ // So the receiver of SignalCandidate can determine which level
+ // (ie; m-line index) the candidate belongs to.
+ void SetLevel(uint16_t level) { level_ = level; }
+
+ uint16_t GetLevel() const { return level_; }
+
+ sigslot::signal2<NrIceMediaStream *, const std::string& >
+ SignalCandidate; // A new ICE candidate:
+
+ sigslot::signal1<NrIceMediaStream *> SignalReady; // Candidate pair ready.
+ sigslot::signal1<NrIceMediaStream *> SignalFailed; // Candidate pair failed.
+ sigslot::signal4<NrIceMediaStream *, int, const unsigned char *, int>
+ SignalPacketReceived; // Incoming packet
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceMediaStream)
+
+ private:
+ NrIceMediaStream(NrIceCtx *ctx,
+ const std::string& name,
+ size_t components);
+
+ ~NrIceMediaStream();
+
+ DISALLOW_COPY_ASSIGN(NrIceMediaStream);
+
+ State state_;
+ nr_ice_ctx *ctx_;
+ nr_ice_peer_ctx *ctx_peer_;
+ const std::string name_;
+ const size_t components_;
+ nr_ice_media_stream *stream_;
+ uint16_t level_;
+ bool has_parsed_attrs_;
+};
+
+
+} // close namespace
+#endif
diff --git a/media/mtransport/nriceresolver.cpp b/media/mtransport/nriceresolver.cpp
new file mode 100644
index 000000000..2298d6faf
--- /dev/null
+++ b/media/mtransport/nriceresolver.cpp
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Original authors: jib@mozilla.com, ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "logging.h"
+#include "nspr.h"
+#include "prnetdb.h"
+
+#include "mozilla/Assertions.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "async_timer.h"
+#include "nr_resolver.h"
+#include "transport_addr.h"
+}
+
+#include "mozilla/net/DNS.h" // TODO(jib@mozilla.com) down here because bug 848578
+#include "nsThreadUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIDNSService.h"
+#include "nsIDNSListener.h"
+#include "nsIDNSRecord.h"
+#include "nsNetCID.h"
+#include "nsCOMPtr.h"
+#include "nriceresolver.h"
+#include "nr_socket_prsock.h"
+#include "mtransport/runnable_utils.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+NrIceResolver::NrIceResolver() :
+ vtbl_(new nr_resolver_vtbl())
+#ifdef DEBUG
+ , allocated_resolvers_(0)
+#endif
+{
+ vtbl_->destroy = &NrIceResolver::destroy;
+ vtbl_->resolve = &NrIceResolver::resolve;
+ vtbl_->cancel = &NrIceResolver::cancel;
+}
+
+NrIceResolver::~NrIceResolver() {
+ MOZ_ASSERT(!allocated_resolvers_);
+ delete vtbl_;
+}
+
+nsresult NrIceResolver::Init() {
+ nsresult rv;
+
+ sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ dns_ = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_ERROR, "Could not acquire DNS service");
+ }
+ return rv;
+}
+
+nr_resolver *NrIceResolver::AllocateResolver() {
+ nr_resolver *resolver;
+
+ int r = nr_resolver_create_int((void *)this, vtbl_, &resolver);
+ MOZ_ASSERT(!r);
+ if(r) {
+ MOZ_MTLOG(ML_ERROR, "nr_resolver_create_int failed");
+ return nullptr;
+ }
+ // We must be available to allocators until they all call DestroyResolver,
+ // because allocators may (and do) outlive the originator of NrIceResolver.
+ AddRef();
+#ifdef DEBUG
+ ++allocated_resolvers_;
+#endif
+ return resolver;
+}
+
+void NrIceResolver::DestroyResolver() {
+#ifdef DEBUG
+ --allocated_resolvers_;
+#endif
+ // Undoes Addref in AllocateResolver so the NrIceResolver can be freed.
+ Release();
+}
+
+int NrIceResolver::destroy(void **objp) {
+ if (!objp || !*objp)
+ return 0;
+ NrIceResolver *resolver = static_cast<NrIceResolver *>(*objp);
+ *objp = 0;
+ resolver->DestroyResolver();
+ return 0;
+}
+
+int NrIceResolver::resolve(void *obj,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle) {
+ MOZ_ASSERT(obj);
+ return static_cast<NrIceResolver *>(obj)->resolve(resource, cb, cb_arg, handle);
+}
+
+int NrIceResolver::resolve(nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle) {
+ int _status;
+ MOZ_ASSERT(allocated_resolvers_ > 0);
+ ASSERT_ON_THREAD(sts_thread_);
+ RefPtr<PendingResolution> pr;
+ uint32_t resolve_flags = 0;
+
+ if (resource->transport_protocol != IPPROTO_UDP &&
+ resource->transport_protocol != IPPROTO_TCP) {
+ MOZ_MTLOG(ML_ERROR, "Only UDP and TCP are supported.");
+ ABORT(R_NOT_FOUND);
+ }
+ pr = new PendingResolution(sts_thread_,
+ resource->port? resource->port : 3478,
+ resource->transport_protocol ?
+ resource->transport_protocol :
+ IPPROTO_UDP,
+ cb, cb_arg);
+
+ switch(resource->address_family) {
+ case AF_INET:
+ resolve_flags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
+ break;
+ case AF_INET6:
+ resolve_flags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (NS_FAILED(dns_->AsyncResolve(nsAutoCString(resource->domain_name),
+ resolve_flags, pr,
+ sts_thread_, getter_AddRefs(pr->request_)))) {
+ MOZ_MTLOG(ML_ERROR, "AsyncResolve failed.");
+ ABORT(R_NOT_FOUND);
+ }
+ // Because the C API offers no "finished" method to release the handle we
+ // return, we cannot return the request we got from AsyncResolve directly.
+ //
+ // Instead, we return an addref'ed reference to PendingResolution itself,
+ // which in turn holds the request and coordinates between cancel and
+ // OnLookupComplete to release it only once.
+ pr.forget(handle);
+
+ _status=0;
+abort:
+ return _status;
+}
+
+nsresult NrIceResolver::PendingResolution::OnLookupComplete(
+ nsICancelable *request, nsIDNSRecord *record, nsresult status) {
+ ASSERT_ON_THREAD(thread_);
+ // First check if we've been canceled. This is single-threaded on the STS
+ // thread, but cancel() cannot guarantee this event isn't on the queue.
+ if (request_) {
+ nr_transport_addr *cb_addr = nullptr;
+ nr_transport_addr ta;
+ // TODO(jib@mozilla.com): Revisit when we do TURN.
+ if (NS_SUCCEEDED(status)) {
+ net::NetAddr na;
+ if (NS_SUCCEEDED(record->GetNextAddr(port_, &na))) {
+ MOZ_ALWAYS_TRUE (nr_netaddr_to_transport_addr(&na, &ta,
+ transport_) == 0);
+ cb_addr = &ta;
+ }
+ }
+ cb_(cb_arg_, cb_addr);
+ request_ = nullptr;
+ Release();
+ }
+ return NS_OK;
+}
+
+int NrIceResolver::cancel(void *obj, void *handle) {
+ MOZ_ALWAYS_TRUE(obj);
+ MOZ_ASSERT(handle);
+ ASSERT_ON_THREAD(static_cast<NrIceResolver *>(obj)->sts_thread_);
+ return static_cast<PendingResolution *>(handle)->cancel();
+}
+
+int NrIceResolver::PendingResolution::cancel() {
+ request_->Cancel (NS_ERROR_ABORT);
+ request_ = nullptr;
+ Release();
+ return 0;
+}
+
+NS_IMPL_ISUPPORTS(NrIceResolver::PendingResolution, nsIDNSListener);
+} // End of namespace mozilla
diff --git a/media/mtransport/nriceresolver.h b/media/mtransport/nriceresolver.h
new file mode 100644
index 000000000..612c860ae
--- /dev/null
+++ b/media/mtransport/nriceresolver.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Original authors: jib@mozilla.com, ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef nriceresolver_h__
+#define nriceresolver_h__
+
+#include <map>
+#include <string>
+#include "nspr.h"
+#include "prnetdb.h"
+#include "nsIDNSService.h"
+#include "nsIDNSListener.h"
+#include "nsICancelable.h"
+
+typedef struct nr_resolver_ nr_resolver;
+typedef struct nr_resolver_vtbl_ nr_resolver_vtbl;
+typedef struct nr_transport_addr_ nr_transport_addr;
+typedef struct nr_resolver_resource_ nr_resolver_resource;
+
+namespace mozilla {
+
+class NrIceResolver
+{
+ private:
+ ~NrIceResolver();
+ public:
+ NrIceResolver();
+
+ nsresult Init();
+ nr_resolver *AllocateResolver();
+ void DestroyResolver();
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceResolver)
+
+ int resolve(nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg, void **handle);
+
+ private:
+ // Implementations of vtbl functions
+ static int destroy(void **objp);
+ static int resolve(void *obj, nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg, void **handle);
+ static void resolve_cb(NR_SOCKET s, int how, void *cb_arg);
+ static int cancel(void *obj, void *handle);
+
+ class PendingResolution : public nsIDNSListener
+ {
+ public:
+ PendingResolution(nsIEventTarget *thread,
+ uint16_t port,
+ int transport,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg) :
+ thread_(thread),
+ port_(port),
+ transport_(transport),
+ cb_(cb), cb_arg_(cb_arg) {}
+ NS_IMETHOD OnLookupComplete(nsICancelable *request, nsIDNSRecord *record,
+ nsresult status) override;
+ int cancel();
+ nsCOMPtr<nsICancelable> request_;
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ private:
+ virtual ~PendingResolution(){};
+ nsCOMPtr<nsIEventTarget> thread_;
+ uint16_t port_;
+ int transport_;
+ int (*cb_)(void *cb_arg, nr_transport_addr *addr);
+ void *cb_arg_;
+ };
+
+ nr_resolver_vtbl* vtbl_;
+ nsCOMPtr<nsIEventTarget> sts_thread_;
+ nsCOMPtr<nsIDNSService> dns_;
+#ifdef DEBUG
+ int allocated_resolvers_;
+#endif
+};
+
+} // End of namespace mozilla
+#endif
diff --git a/media/mtransport/nriceresolverfake.cpp b/media/mtransport/nriceresolverfake.cpp
new file mode 100644
index 000000000..94a52fd3c
--- /dev/null
+++ b/media/mtransport/nriceresolverfake.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Original author: ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "nspr.h"
+#include "prnetdb.h"
+
+#include "mozilla/Assertions.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "async_timer.h"
+#include "nr_resolver.h"
+#include "transport_addr.h"
+}
+
+#include "nriceresolverfake.h"
+#include "nr_socket_prsock.h"
+
+namespace mozilla {
+
+NrIceResolverFake::NrIceResolverFake() :
+ vtbl_(new nr_resolver_vtbl), addrs_(), delay_ms_(100),
+ allocated_resolvers_(0) {
+ vtbl_->destroy = &NrIceResolverFake::destroy;
+ vtbl_->resolve = &NrIceResolverFake::resolve;
+ vtbl_->cancel = &NrIceResolverFake::cancel;
+}
+
+NrIceResolverFake::~NrIceResolverFake() {
+ MOZ_ASSERT(allocated_resolvers_ == 0);
+ delete vtbl_;
+}
+
+
+nr_resolver *NrIceResolverFake::AllocateResolver() {
+ nr_resolver *resolver;
+
+ int r = nr_resolver_create_int((void *)this,
+ vtbl_, &resolver);
+ MOZ_ASSERT(!r);
+ if(r)
+ return nullptr;
+
+ ++allocated_resolvers_;
+
+ return resolver;
+}
+
+void NrIceResolverFake::DestroyResolver() {
+ --allocated_resolvers_;
+}
+
+int NrIceResolverFake::destroy(void **objp) {
+ if (!objp || !*objp)
+ return 0;
+
+ NrIceResolverFake *fake = static_cast<NrIceResolverFake *>(*objp);
+ *objp = 0;
+
+ fake->DestroyResolver();
+
+ return 0;
+}
+
+int NrIceResolverFake::resolve(void *obj,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg,
+ nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle) {
+ int r,_status;
+
+ MOZ_ASSERT(obj);
+ NrIceResolverFake *fake = static_cast<NrIceResolverFake *>(obj);
+
+ MOZ_ASSERT(fake->allocated_resolvers_ > 0);
+
+ PendingResolution *pending =
+ new PendingResolution(fake,
+ resource->domain_name,
+ resource->port ? resource->port : 3478,
+ resource->transport_protocol ?
+ resource->transport_protocol :
+ IPPROTO_UDP,
+ resource->address_family,
+ cb, cb_arg);
+
+ if ((r=NR_ASYNC_TIMER_SET(fake->delay_ms_,NrIceResolverFake::resolve_cb,
+ (void *)pending, &pending->timer_handle_))) {
+ delete pending;
+ ABORT(r);
+ }
+ *handle = pending;
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+void NrIceResolverFake::resolve_cb(NR_SOCKET s, int how, void *cb_arg) {
+ MOZ_ASSERT(cb_arg);
+ PendingResolution *pending = static_cast<PendingResolution *>(cb_arg);
+
+ const PRNetAddr *addr=pending->resolver_->Resolve(pending->hostname_,
+ pending->address_family_);
+
+ if (addr) {
+ nr_transport_addr transport_addr;
+
+ int r = nr_praddr_to_transport_addr(addr, &transport_addr,
+ pending->transport_, 0);
+ MOZ_ASSERT(!r);
+ if (r)
+ goto abort;
+
+ r=nr_transport_addr_set_port(&transport_addr, pending->port_);
+ MOZ_ASSERT(!r);
+ if (r)
+ goto abort;
+
+ /* Fill in the address string */
+ r=nr_transport_addr_fmt_addr_string(&transport_addr);
+ MOZ_ASSERT(!r);
+ if (r)
+ goto abort;
+
+ pending->cb_(pending->cb_arg_, &transport_addr);
+ delete pending;
+ return;
+ }
+
+abort:
+ // Resolution failed.
+ pending->cb_(pending->cb_arg_, nullptr);
+
+ delete pending;
+}
+
+int NrIceResolverFake::cancel(void *obj, void *handle) {
+ MOZ_ASSERT(obj);
+ MOZ_ASSERT(static_cast<NrIceResolverFake *>(obj)->allocated_resolvers_ > 0);
+
+ PendingResolution *pending = static_cast<PendingResolution *>(handle);
+
+ NR_async_timer_cancel(pending->timer_handle_);
+ delete pending;
+
+ return(0);
+}
+
+
+} // End of namespace mozilla
diff --git a/media/mtransport/nriceresolverfake.h b/media/mtransport/nriceresolverfake.h
new file mode 100644
index 000000000..a37811fc9
--- /dev/null
+++ b/media/mtransport/nriceresolverfake.h
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// Original author: ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef nriceresolverfake_h__
+#define nriceresolverfake_h__
+
+#include <map>
+#include <string>
+
+#include "nspr.h"
+#include "prnetdb.h"
+
+typedef struct nr_resolver_ nr_resolver;
+typedef struct nr_resolver_vtbl_ nr_resolver_vtbl;
+typedef struct nr_transport_addr_ nr_transport_addr;
+typedef struct nr_resolver_resource_ nr_resolver_resource;
+
+namespace mozilla {
+
+class NrIceResolverFake {
+ public:
+ NrIceResolverFake();
+ ~NrIceResolverFake();
+
+ void SetAddr(const std::string& hostname, const PRNetAddr& addr) {
+ switch (addr.raw.family) {
+ case AF_INET:
+ addrs_[hostname] = addr;
+ break;
+ case AF_INET6:
+ addrs6_[hostname] = addr;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ }
+
+ nr_resolver *AllocateResolver();
+
+ void DestroyResolver();
+
+private:
+ // Implementations of vtbl functions
+ static int destroy(void **objp);
+ static int resolve(void *obj,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg,
+ nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle);
+ static void resolve_cb(NR_SOCKET s, int how, void *cb_arg);
+ static int cancel(void *obj, void *handle);
+
+ // Get an address.
+ const PRNetAddr *Resolve(const std::string& hostname, int address_family) {
+ switch (address_family) {
+ case AF_INET:
+ if (!addrs_.count(hostname))
+ return nullptr;
+
+ return &addrs_[hostname];
+ case AF_INET6:
+ if (!addrs6_.count(hostname))
+ return nullptr;
+
+ return &addrs6_[hostname];
+ default:
+ MOZ_CRASH();
+ }
+ }
+
+
+ struct PendingResolution {
+ PendingResolution(NrIceResolverFake *resolver,
+ const std::string& hostname,
+ uint16_t port,
+ int transport,
+ int address_family,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg) :
+ resolver_(resolver),
+ hostname_(hostname),
+ port_(port),
+ transport_(transport),
+ address_family_(address_family),
+ cb_(cb), cb_arg_(cb_arg) {}
+
+ NrIceResolverFake *resolver_;
+ std::string hostname_;
+ uint16_t port_;
+ int transport_;
+ int address_family_;
+ int (*cb_)(void *cb_arg, nr_transport_addr *addr);
+ void *cb_arg_;
+ void *timer_handle_;
+ };
+
+ nr_resolver_vtbl* vtbl_;
+ std::map<std::string, PRNetAddr> addrs_;
+ std::map<std::string, PRNetAddr> addrs6_;
+ uint32_t delay_ms_;
+ int allocated_resolvers_;
+};
+
+} // End of namespace mozilla
+
+#endif
diff --git a/media/mtransport/nrinterfaceprioritizer.cpp b/media/mtransport/nrinterfaceprioritizer.cpp
new file mode 100644
index 000000000..90ab1bc60
--- /dev/null
+++ b/media/mtransport/nrinterfaceprioritizer.cpp
@@ -0,0 +1,268 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <algorithm>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include "logging.h"
+#include "nrinterfaceprioritizer.h"
+#include "nsCOMPtr.h"
+
+MOZ_MTLOG_MODULE("mtransport")
+
+namespace {
+
+class LocalAddress {
+public:
+ LocalAddress()
+ : ifname_(),
+ addr_(),
+ key_(),
+ is_vpn_(-1),
+ estimated_speed_(-1),
+ type_preference_(-1),
+ ip_version_(-1) {}
+
+ bool Init(const nr_local_addr& local_addr) {
+ ifname_ = local_addr.addr.ifname;
+
+ char buf[MAXIFNAME + 41];
+ int r = nr_transport_addr_fmt_ifname_addr_string(&local_addr.addr, buf, sizeof(buf));
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Error formatting interface key.");
+ return false;
+ }
+ key_ = buf;
+
+ r = nr_transport_addr_get_addrstring(&local_addr.addr, buf, sizeof(buf));
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Error formatting address string.");
+ return false;
+ }
+ addr_ = buf;
+
+ is_vpn_ = (local_addr.interface.type & NR_INTERFACE_TYPE_VPN) != 0 ? 1 : 0;
+ estimated_speed_ = local_addr.interface.estimated_speed;
+ type_preference_ = GetNetworkTypePreference(local_addr.interface.type);
+ ip_version_ = local_addr.addr.ip_version;
+ return true;
+ }
+
+ bool operator<(const LocalAddress& rhs) const {
+ // Interface that is "less" here is preferred.
+ // If type preferences are different, we should simply sort by
+ // |type_preference_|.
+ if (type_preference_ != rhs.type_preference_) {
+ return type_preference_ < rhs.type_preference_;
+ }
+
+ // If type preferences are the same, the next thing we use to sort is vpn.
+ // If two LocalAddress are different in |is_vpn_|, the LocalAddress that is
+ // not in vpn gets priority.
+ if (is_vpn_ != rhs.is_vpn_) {
+ return is_vpn_ < rhs.is_vpn_;
+ }
+
+ // Compare estimated speed.
+ if (estimated_speed_ != rhs.estimated_speed_) {
+ return estimated_speed_ > rhs.estimated_speed_;
+ }
+
+ // See if our hard-coded pref list helps us.
+ auto thisindex = std::find(interface_preference_list().begin(),
+ interface_preference_list().end(),
+ ifname_);
+ auto rhsindex = std::find(interface_preference_list().begin(),
+ interface_preference_list().end(),
+ rhs.ifname_);
+ if (thisindex != rhsindex) {
+ return thisindex < rhsindex;
+ }
+
+ // Prefer IPV6 over IPV4
+ if (ip_version_ != rhs.ip_version_) {
+ return ip_version_ > rhs.ip_version_;
+ }
+
+ // Now we start getting into arbitrary stuff
+ if (ifname_ != rhs.ifname_) {
+ return ifname_ < rhs.ifname_;
+ }
+
+ return addr_ < rhs.addr_;
+ }
+
+ const std::string& GetKey() const {
+ return key_;
+ }
+
+private:
+ // Getting the preference corresponding to a type. Getting lower number here
+ // means the type of network is preferred.
+ static inline int GetNetworkTypePreference(int type) {
+ if (type & NR_INTERFACE_TYPE_WIRED) {
+ return 1;
+ }
+ if (type & NR_INTERFACE_TYPE_WIFI) {
+ return 2;
+ }
+ if (type & NR_INTERFACE_TYPE_MOBILE) {
+ return 3;
+ }
+ return 4;
+ }
+
+ // TODO(bug 895790): Once we can get useful interface properties on Darwin,
+ // we should remove this stuff.
+ static const std::vector<std::string>& interface_preference_list()
+ {
+ static std::vector<std::string> list(build_interface_preference_list());
+ return list;
+ }
+
+ static std::vector<std::string> build_interface_preference_list()
+ {
+ std::vector<std::string> result;
+ result.push_back("rl0");
+ result.push_back("wi0");
+ result.push_back("en0");
+ result.push_back("enp2s0");
+ result.push_back("enp3s0");
+ result.push_back("en1");
+ result.push_back("en2");
+ result.push_back("en3");
+ result.push_back("eth0");
+ result.push_back("eth1");
+ result.push_back("eth2");
+ result.push_back("em1");
+ result.push_back("em0");
+ result.push_back("ppp");
+ result.push_back("ppp0");
+ result.push_back("vmnet1");
+ result.push_back("vmnet0");
+ result.push_back("vmnet3");
+ result.push_back("vmnet4");
+ result.push_back("vmnet5");
+ result.push_back("vmnet6");
+ result.push_back("vmnet7");
+ result.push_back("vmnet8");
+ result.push_back("virbr0");
+ result.push_back("wlan0");
+ result.push_back("lo0");
+ return result;
+ }
+
+ std::string ifname_;
+ std::string addr_;
+ std::string key_;
+ int is_vpn_;
+ int estimated_speed_;
+ int type_preference_;
+ int ip_version_;
+};
+
+class InterfacePrioritizer {
+public:
+ InterfacePrioritizer()
+ : local_addrs_(),
+ preference_map_(),
+ sorted_(false) {}
+
+ int add(const nr_local_addr *iface) {
+ LocalAddress addr;
+ if (!addr.Init(*iface)) {
+ return R_FAILED;
+ }
+ std::pair<std::set<LocalAddress>::iterator, bool> r =
+ local_addrs_.insert(addr);
+ if (!r.second) {
+ return R_ALREADY; // This address is already in the set.
+ }
+ sorted_ = false;
+ return 0;
+ }
+
+ int sort() {
+ UCHAR tmp_pref = 127;
+ preference_map_.clear();
+ for (std::set<LocalAddress>::iterator i = local_addrs_.begin();
+ i != local_addrs_.end(); ++i) {
+ if (tmp_pref == 0) {
+ return R_FAILED;
+ }
+ preference_map_.insert(make_pair(i->GetKey(), tmp_pref--));
+ }
+ sorted_ = true;
+ return 0;
+ }
+
+ int getPreference(const char *key, UCHAR *pref) {
+ if (!sorted_) {
+ return R_FAILED;
+ }
+ std::map<std::string, UCHAR>::iterator i = preference_map_.find(key);
+ if (i == preference_map_.end()) {
+ return R_NOT_FOUND;
+ }
+ *pref = i->second;
+ return 0;
+ }
+
+private:
+ std::set<LocalAddress> local_addrs_;
+ std::map<std::string, UCHAR> preference_map_;
+ bool sorted_;
+};
+
+} // anonymous namespace
+
+static int add_interface(void *obj, nr_local_addr *iface) {
+ InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj);
+ return ip->add(iface);
+}
+
+static int get_priority(void *obj, const char *key, UCHAR *pref) {
+ InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj);
+ return ip->getPreference(key, pref);
+}
+
+static int sort_preference(void *obj) {
+ InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(obj);
+ return ip->sort();
+}
+
+static int destroy(void **objp) {
+ if (!objp || !*objp) {
+ return 0;
+ }
+
+ InterfacePrioritizer *ip = static_cast<InterfacePrioritizer*>(*objp);
+ *objp = 0;
+ delete ip;
+
+ return 0;
+}
+
+static nr_interface_prioritizer_vtbl priorizer_vtbl = {
+ add_interface,
+ get_priority,
+ sort_preference,
+ destroy
+};
+
+namespace mozilla {
+
+nr_interface_prioritizer* CreateInterfacePrioritizer() {
+ nr_interface_prioritizer *ip;
+ int r = nr_interface_prioritizer_create_int(new InterfacePrioritizer(),
+ &priorizer_vtbl,
+ &ip);
+ if (r != 0) {
+ return nullptr;
+ }
+ return ip;
+}
+
+} // namespace mozilla
diff --git a/media/mtransport/nrinterfaceprioritizer.h b/media/mtransport/nrinterfaceprioritizer.h
new file mode 100644
index 000000000..40a9f0e66
--- /dev/null
+++ b/media/mtransport/nrinterfaceprioritizer.h
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nrinterfacepriority_h__
+#define nrinterfacepriority_h__
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_interface_prioritizer.h"
+}
+
+namespace mozilla {
+
+nr_interface_prioritizer* CreateInterfacePrioritizer();
+
+} // namespace mozilla
+
+#endif
diff --git a/media/mtransport/rlogconnector.cpp b/media/mtransport/rlogconnector.cpp
new file mode 100644
index 000000000..8110f4ea7
--- /dev/null
+++ b/media/mtransport/rlogconnector.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Original author: bcampen@mozilla.com */
+
+#include <cstdarg>
+
+#include "rlogconnector.h"
+
+#include <deque>
+#include <string>
+#include "logging.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h" // Pinch hitting for <utility> and std::move
+#include "mozilla/Mutex.h"
+#include "mozilla/Sprintf.h"
+#include <vector>
+
+extern "C" {
+#include <csi_platform.h>
+#include "r_log.h"
+#include "registry.h"
+}
+
+/* Matches r_dest_vlog type defined in r_log.h */
+static int ringbuffer_vlog(int facility,
+ int level,
+ const char *format,
+ va_list ap) {
+ MOZ_ASSERT(mozilla::RLogConnector::GetInstance());
+ // I could be evil and printf right into a std::string, but unless this
+ // shows up in profiling, it is not worth doing.
+ char temp[4096];
+ VsprintfLiteral(temp, format, ap);
+
+ mozilla::RLogConnector::GetInstance()->Log(level, std::string(temp));
+ return 0;
+}
+
+static mozilla::LogLevel rLogLvlToMozLogLvl(int level) {
+ switch (level) {
+ case LOG_EMERG:
+ case LOG_ALERT:
+ case LOG_CRIT:
+ case LOG_ERR:
+ return mozilla::LogLevel::Error;
+ case LOG_WARNING:
+ return mozilla::LogLevel::Warning;
+ case LOG_NOTICE:
+ return mozilla::LogLevel::Info;
+ case LOG_INFO:
+ return mozilla::LogLevel::Debug;
+ case LOG_DEBUG:
+ default:
+ return mozilla::LogLevel::Verbose;
+ }
+}
+
+MOZ_MTLOG_MODULE("nicer");
+
+namespace mozilla {
+
+RLogConnector* RLogConnector::instance;
+
+RLogConnector::RLogConnector()
+ : log_limit_(4096),
+ mutex_("RLogConnector::mutex_"),
+ disableCount_(0) {
+}
+
+RLogConnector::~RLogConnector() {
+}
+
+void RLogConnector::SetLogLimit(uint32_t new_limit) {
+ OffTheBooksMutexAutoLock lock(mutex_);
+ log_limit_ = new_limit;
+ RemoveOld();
+}
+
+void RLogConnector::Log(int level, std::string&& log) {
+ MOZ_MTLOG(rLogLvlToMozLogLvl(level), log);
+
+ if (level <= LOG_INFO) {
+ OffTheBooksMutexAutoLock lock(mutex_);
+ if (disableCount_ == 0) {
+ AddMsg(Move(log));
+ }
+ }
+}
+
+void RLogConnector::AddMsg(std::string&& msg) {
+ log_messages_.push_front(Move(msg));
+ RemoveOld();
+}
+
+inline void RLogConnector::RemoveOld() {
+ if (log_messages_.size() > log_limit_) {
+ log_messages_.resize(log_limit_);
+ }
+}
+
+RLogConnector* RLogConnector::CreateInstance() {
+ if (!instance) {
+ instance = new RLogConnector;
+ NR_reg_init(NR_REG_MODE_LOCAL);
+ r_log_set_extra_destination(LOG_DEBUG, &ringbuffer_vlog);
+ }
+ return instance;
+}
+
+RLogConnector* RLogConnector::GetInstance() {
+ return instance;
+}
+
+void RLogConnector::DestroyInstance() {
+ // First param is ignored when passing null
+ r_log_set_extra_destination(LOG_DEBUG, nullptr);
+ delete instance;
+ instance = nullptr;
+}
+
+// As long as at least one PeerConnection exists in a Private Window rlog messages will not
+// be saved in the RLogConnector. This is necessary because the log_messages buffer
+// is shared across all instances of PeerConnectionImpls. There is no way with the current
+// structure of r_log to run separate logs.
+
+void RLogConnector::EnterPrivateMode() {
+ OffTheBooksMutexAutoLock lock(mutex_);
+ ++disableCount_;
+ MOZ_ASSERT(disableCount_ != 0);
+
+ if (disableCount_ == 1) {
+ AddMsg("LOGGING SUSPENDED: a connection is active in a Private Window ***");
+ }
+}
+
+void RLogConnector::ExitPrivateMode() {
+ OffTheBooksMutexAutoLock lock(mutex_);
+ MOZ_ASSERT(disableCount_ != 0);
+
+ if (--disableCount_ == 0) {
+ AddMsg("LOGGING RESUMED: no connections are active in a Private Window ***");
+ }
+}
+
+void RLogConnector::Clear() {
+ OffTheBooksMutexAutoLock lock(mutex_);
+ log_messages_.clear();
+}
+
+void RLogConnector::Filter(const std::string& substring,
+ uint32_t limit,
+ std::deque<std::string>* matching_logs) {
+ std::vector<std::string> substrings;
+ substrings.push_back(substring);
+ FilterAny(substrings, limit, matching_logs);
+}
+
+inline bool AnySubstringMatches(const std::vector<std::string>& substrings,
+ const std::string& string) {
+ for (auto sub = substrings.begin(); sub != substrings.end(); ++sub) {
+ if (string.find(*sub) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void RLogConnector::FilterAny(const std::vector<std::string>& substrings,
+ uint32_t limit,
+ std::deque<std::string>* matching_logs) {
+ OffTheBooksMutexAutoLock lock(mutex_);
+ if (limit == 0) {
+ // At a max, all of the log messages.
+ limit = log_limit_;
+ }
+
+ for (auto log = log_messages_.begin();
+ log != log_messages_.end() && matching_logs->size() < limit;
+ ++log) {
+ if (AnySubstringMatches(substrings, *log)) {
+ matching_logs->push_front(*log);
+ }
+ }
+}
+
+} // namespace mozilla
diff --git a/media/mtransport/rlogconnector.h b/media/mtransport/rlogconnector.h
new file mode 100644
index 000000000..ef81c166a
--- /dev/null
+++ b/media/mtransport/rlogconnector.h
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Original author: bcampen@mozilla.com */
+
+/*
+ This file defines an r_dest_vlog that can be used to accumulate log messages
+ for later inspection/filtering. The intent is to use this for interactive
+ debug purposes on an about:webrtc page or similar.
+*/
+
+#ifndef rlogconnector_h__
+#define rlogconnector_h__
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "mozilla/Mutex.h"
+
+#include "m_cpp_utils.h"
+
+namespace mozilla {
+
+class RLogConnector {
+ public:
+ /*
+ NB: These are not threadsafe, nor are they safe to call during static
+ init/deinit.
+ */
+ static RLogConnector* CreateInstance();
+ static RLogConnector* GetInstance();
+ static void DestroyInstance();
+
+ /*
+ Retrieves log statements that match a given substring, subject to a
+ limit. |matching_logs| will be filled in chronological order (front()
+ is oldest, back() is newest). |limit| == 0 will be interpreted as no
+ limit.
+ */
+ void Filter(const std::string& substring,
+ uint32_t limit,
+ std::deque<std::string>* matching_logs);
+
+ void FilterAny(const std::vector<std::string>& substrings,
+ uint32_t limit,
+ std::deque<std::string>* matching_logs);
+
+ inline void GetAny(uint32_t limit,
+ std::deque<std::string>* matching_logs) {
+ Filter("", limit, matching_logs);
+ }
+
+ void SetLogLimit(uint32_t new_limit);
+ void Log(int level, std::string&& log);
+ void Clear();
+
+ // Methods to signal when a PeerConnection exists in a Private Window.
+ void EnterPrivateMode();
+ void ExitPrivateMode();
+
+ private:
+ RLogConnector();
+ ~RLogConnector();
+ void RemoveOld();
+ void AddMsg(std::string&& msg);
+
+ static RLogConnector* instance;
+
+ /*
+ * Might be worthwhile making this a circular buffer, but I think it is
+ * preferable to take up as little space as possible if no logging is
+ * happening/the ringbuffer is not being used.
+ */
+ std::deque<std::string> log_messages_;
+ /* Max size of log buffer (should we use time-depth instead/also?) */
+ uint32_t log_limit_;
+ OffTheBooksMutex mutex_;
+ uint32_t disableCount_;
+
+ DISALLOW_COPY_ASSIGN(RLogConnector);
+}; // class RLogConnector
+
+} // namespace mozilla
+
+#endif // rlogconnector_h__
diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h
new file mode 100644
index 000000000..0d7b1ce6f
--- /dev/null
+++ b/media/mtransport/runnable_utils.h
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef runnable_utils_h__
+#define runnable_utils_h__
+
+#include "nsThreadUtils.h"
+#include "mozilla/IndexSequence.h"
+#include "mozilla/Move.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Tuple.h"
+
+// Abstract base class for all of our templates
+namespace mozilla {
+
+namespace detail {
+
+enum RunnableResult {
+ NoResult,
+ ReturnsResult
+};
+
+static inline nsresult
+RunOnThreadInternal(nsIEventTarget *thread, nsIRunnable *runnable, uint32_t flags)
+{
+ nsCOMPtr<nsIRunnable> runnable_ref(runnable);
+ if (thread) {
+ bool on;
+ nsresult rv;
+ rv = thread->IsOnCurrentThread(&on);
+
+ // If the target thread has already shut down, we don't want to assert.
+ if (rv != NS_ERROR_NOT_INITIALIZED) {
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // we're going to destroy the runnable on this thread!
+ return rv;
+ }
+ if (!on) {
+ return thread->Dispatch(runnable_ref.forget(), flags);
+ }
+ }
+ return runnable_ref->Run();
+}
+
+template<RunnableResult result>
+class runnable_args_base : public Runnable {
+ public:
+ NS_IMETHOD Run() = 0;
+};
+
+
+template<typename R>
+struct RunnableFunctionCallHelper
+{
+ template<typename FunType, typename... Args, size_t... Indices>
+ static R apply(FunType func, Tuple<Args...>& args, IndexSequence<Indices...>)
+ {
+ return func(Get<Indices>(args)...);
+ }
+};
+
+// A void specialization is needed in the case where the template instantiator
+// knows we don't want to return a value, but we don't know whether the called
+// function returns void or something else.
+template<>
+struct RunnableFunctionCallHelper<void>
+{
+ template<typename FunType, typename... Args, size_t... Indices>
+ static void apply(FunType func, Tuple<Args...>& args, IndexSequence<Indices...>)
+ {
+ func(Get<Indices>(args)...);
+ }
+};
+
+template<typename R>
+struct RunnableMethodCallHelper
+{
+ template<typename Class, typename M, typename... Args, size_t... Indices>
+ static R apply(Class obj, M method, Tuple<Args...>& args, IndexSequence<Indices...>)
+ {
+ return ((*obj).*method)(Get<Indices>(args)...);
+ }
+};
+
+// A void specialization is needed in the case where the template instantiator
+// knows we don't want to return a value, but we don't know whether the called
+// method returns void or something else.
+template<>
+struct RunnableMethodCallHelper<void>
+{
+ template<typename Class, typename M, typename... Args, size_t... Indices>
+ static void apply(Class obj, M method, Tuple<Args...>& args, IndexSequence<Indices...>)
+ {
+ ((*obj).*method)(Get<Indices>(args)...);
+ }
+};
+
+}
+
+template<typename FunType, typename... Args>
+class runnable_args_func : public detail::runnable_args_base<detail::NoResult>
+{
+public:
+ // |explicit| to pacify static analysis when there are no |args|.
+ explicit runnable_args_func(FunType f, Args&&... args)
+ : mFunc(f), mArgs(Forward<Args>(args)...)
+ {}
+
+ NS_IMETHOD Run() {
+ detail::RunnableFunctionCallHelper<void>::apply(mFunc, mArgs, typename IndexSequenceFor<Args...>::Type());
+ return NS_OK;
+ }
+
+private:
+ FunType mFunc;
+ Tuple<Args...> mArgs;
+};
+
+template<typename FunType, typename... Args>
+runnable_args_func<FunType, Args...>*
+WrapRunnableNM(FunType f, Args... args)
+{
+ return new runnable_args_func<FunType, Args...>(f, Move(args)...);
+}
+
+template<typename Ret, typename FunType, typename... Args>
+class runnable_args_func_ret : public detail::runnable_args_base<detail::ReturnsResult>
+{
+public:
+ runnable_args_func_ret(Ret* ret, FunType f, Args&&... args)
+ : mReturn(ret), mFunc(f), mArgs(Forward<Args>(args)...)
+ {}
+
+ NS_IMETHOD Run() {
+ *mReturn = detail::RunnableFunctionCallHelper<Ret>::apply(mFunc, mArgs, typename IndexSequenceFor<Args...>::Type());
+ return NS_OK;
+ }
+
+private:
+ Ret* mReturn;
+ FunType mFunc;
+ Tuple<Args...> mArgs;
+};
+
+template<typename R, typename FunType, typename... Args>
+runnable_args_func_ret<R, FunType, Args...>*
+WrapRunnableNMRet(R* ret, FunType f, Args... args)
+{
+ return new runnable_args_func_ret<R, FunType, Args...>(ret, f, Move(args)...);
+}
+
+template<typename Class, typename M, typename... Args>
+class runnable_args_memfn : public detail::runnable_args_base<detail::NoResult>
+{
+public:
+ runnable_args_memfn(Class obj, M method, Args&&... args)
+ : mObj(obj), mMethod(method), mArgs(Forward<Args>(args)...)
+ {}
+
+ NS_IMETHOD Run() {
+ detail::RunnableMethodCallHelper<void>::apply(mObj, mMethod, mArgs, typename IndexSequenceFor<Args...>::Type());
+ return NS_OK;
+ }
+
+private:
+ Class mObj;
+ M mMethod;
+ Tuple<Args...> mArgs;
+};
+
+template<typename Class, typename M, typename... Args>
+runnable_args_memfn<Class, M, Args...>*
+WrapRunnable(Class obj, M method, Args... args)
+{
+ return new runnable_args_memfn<Class, M, Args...>(obj, method, Move(args)...);
+}
+
+template<typename Ret, typename Class, typename M, typename... Args>
+class runnable_args_memfn_ret : public detail::runnable_args_base<detail::ReturnsResult>
+{
+public:
+ runnable_args_memfn_ret(Ret* ret, Class obj, M method, Args... args)
+ : mReturn(ret), mObj(obj), mMethod(method), mArgs(Forward<Args>(args)...)
+ {}
+
+ NS_IMETHOD Run() {
+ *mReturn = detail::RunnableMethodCallHelper<Ret>::apply(mObj, mMethod, mArgs, typename IndexSequenceFor<Args...>::Type());
+ return NS_OK;
+ }
+
+private:
+ Ret* mReturn;
+ Class mObj;
+ M mMethod;
+ Tuple<Args...> mArgs;
+};
+
+template<typename R, typename Class, typename M, typename... Args>
+runnable_args_memfn_ret<R, Class, M, Args...>*
+WrapRunnableRet(R* ret, Class obj, M method, Args... args)
+{
+ return new runnable_args_memfn_ret<R, Class, M, Args...>(ret, obj, method, Move(args)...);
+}
+
+static inline nsresult RUN_ON_THREAD(nsIEventTarget *thread, detail::runnable_args_base<detail::NoResult> *runnable, uint32_t flags) {
+ return detail::RunOnThreadInternal(thread, static_cast<nsIRunnable *>(runnable), flags);
+}
+
+static inline nsresult
+RUN_ON_THREAD(nsIEventTarget *thread, detail::runnable_args_base<detail::ReturnsResult> *runnable)
+{
+ return detail::RunOnThreadInternal(thread, static_cast<nsIRunnable *>(runnable), NS_DISPATCH_SYNC);
+}
+
+#ifdef DEBUG
+#define ASSERT_ON_THREAD(t) do { \
+ if (t) { \
+ bool on; \
+ nsresult rv; \
+ rv = t->IsOnCurrentThread(&on); \
+ MOZ_ASSERT(NS_SUCCEEDED(rv)); \
+ MOZ_ASSERT(on); \
+ } \
+ } while(0)
+#else
+#define ASSERT_ON_THREAD(t)
+#endif
+
+template <class T>
+class DispatchedRelease : public detail::runnable_args_base<detail::NoResult> {
+public:
+ explicit DispatchedRelease(already_AddRefed<T>& ref) : ref_(ref) {}
+
+ NS_IMETHOD Run() {
+ ref_ = nullptr;
+ return NS_OK;
+ }
+private:
+ RefPtr<T> ref_;
+};
+
+template <typename T>
+DispatchedRelease<T>* WrapRelease(already_AddRefed<T>&& ref)
+{
+ return new DispatchedRelease<T>(ref);
+}
+
+} /* namespace mozilla */
+
+#endif
diff --git a/media/mtransport/sigslot.h b/media/mtransport/sigslot.h
new file mode 100644
index 000000000..91cd76f25
--- /dev/null
+++ b/media/mtransport/sigslot.h
@@ -0,0 +1,2859 @@
+// sigslot.h: Signal/Slot classes
+//
+// Written by Sarah Thompson (sarah@telergy.com) 2002.
+//
+// License: Public domain. You are free to use this code however you like, with the proviso that
+// the author takes on no responsibility or liability for any use.
+//
+// QUICK DOCUMENTATION
+//
+// (see also the full documentation at http://sigslot.sourceforge.net/)
+//
+// #define switches
+// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables
+// all of the thread safety support on platforms where it is
+// available.
+//
+// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than
+// gcc on a platform that supports Posix threads. (When using gcc,
+// this is the default - use SIGSLOT_PURE_ISO to disable this if
+// necessary)
+//
+// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global.
+// Otherwise, the default is single_threaded. #define this yourself to
+// override the default. In pure ISO mode, anything other than
+// single_threaded will cause a compiler error.
+//
+// PLATFORM NOTES
+//
+// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream
+// compilers do this by default, but you may need to define it
+// yourself if your build environment is less standard. This causes
+// the Win32 thread support to be compiled in and used automatically.
+//
+// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads
+// available, so they are used automatically. You can override this
+// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using
+// something other than gcc but still want to use Posix threads, you
+// need to #define SIGSLOT_USE_POSIX_THREADS.
+//
+// ISO C++ - If none of the supported platforms are detected, or if
+// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off,
+// along with any code that might cause a pure ISO C++ environment to
+// complain. Before you ask, gcc -ansi -pedantic won't compile this
+// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of
+// errors that aren't really there. If you feel like investigating this,
+// please contact the author.
+//
+//
+// THREADING MODES
+//
+// single_threaded - Your program is assumed to be single threaded from the point of view
+// of signal/slot usage (i.e. all objects using signals and slots are
+// created and destroyed from a single thread). Behaviour if objects are
+// destroyed concurrently is undefined (i.e. you'll get the occasional
+// segmentation fault/memory exception).
+//
+// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and
+// slots can be safely created and destroyed from any thread, even when
+// connections exist. In multi_threaded_global mode, this is achieved by a
+// single global mutex (actually a critical section on Windows because they
+// are faster). This option uses less OS resources, but results in more
+// opportunities for contention, possibly resulting in more context switches
+// than are strictly necessary.
+//
+// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global,
+// except that each signal, and each object that inherits has_slots, all
+// have their own mutex/critical section. In practice, this means that
+// mutex collisions (and hence context switches) only happen if they are
+// absolutely essential. However, on some platforms, creating a lot of
+// mutexes can slow down the whole OS, so use this option with care.
+//
+// USING THE LIBRARY
+//
+// See the full documentation at http://sigslot.sourceforge.net/
+//
+//
+// Libjingle specific:
+// This file has been modified such that has_slots and signalx do not have to be
+// using the same threading requirements. E.g. it is possible to connect a
+// has_slots<single_threaded> and signal0<multi_threaded_local> or
+// has_slots<multi_threaded_local> and signal0<single_threaded>.
+// If has_slots is single threaded the user must ensure that it is not trying
+// to connect or disconnect to signalx concurrently or data race may occur.
+// If signalx is single threaded the user must ensure that disconnect, connect
+// or signal is not happening concurrently or data race may occur.
+
+#ifndef TALK_BASE_SIGSLOT_H__
+#define TALK_BASE_SIGSLOT_H__
+
+#include <list>
+#include <set>
+#include <stdlib.h>
+
+// On our copy of sigslot.h, we set single threading as default.
+#define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+
+// For now, this avoids windows.h in bindings (PeerConnectionImpl.h) on WIN32
+// TODO: wrap win32 crit section and move to another file instead (Bug 932570)
+#define SIGSLOT_LEAVE_OUT_MULTITHREADING 1
+
+#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS))
+# define _SIGSLOT_SINGLE_THREADED
+#elif defined(WIN32)
+# define _SIGSLOT_HAS_WIN32_THREADS
+# ifndef SIGSLOT_LEAVE_OUT_MULTITHREADING
+# if !defined(WIN32_LEAN_AND_MEAN)
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include "windows.h"
+# endif
+#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
+# define _SIGSLOT_HAS_POSIX_THREADS
+# include <pthread.h>
+#else
+# define _SIGSLOT_SINGLE_THREADED
+#endif
+
+#ifndef SIGSLOT_DEFAULT_MT_POLICY
+# ifdef _SIGSLOT_SINGLE_THREADED
+# define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+# else
+# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
+# endif
+#endif
+
+// TODO: change this namespace to talk_base?
+namespace sigslot {
+
+ class single_threaded
+ {
+ public:
+ single_threaded()
+ {
+ ;
+ }
+
+ virtual ~single_threaded()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ ;
+ }
+
+ virtual void unlock()
+ {
+ ;
+ }
+ };
+
+#ifndef SIGSLOT_LEAVE_OUT_MULTITHREADING
+# ifdef _SIGSLOT_HAS_WIN32_THREADS
+
+ // The multi threading policies only get compiled in if they are enabled.
+ class multi_threaded_global
+ {
+ public:
+ multi_threaded_global()
+ {
+ static bool isinitialised = false;
+
+ if(!isinitialised)
+ {
+ InitializeCriticalSection(get_critsec());
+ isinitialised = true;
+ }
+ }
+
+ multi_threaded_global(const multi_threaded_global&)
+ {
+ ;
+ }
+
+ virtual ~multi_threaded_global()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ EnterCriticalSection(get_critsec());
+ }
+
+ virtual void unlock()
+ {
+ LeaveCriticalSection(get_critsec());
+ }
+
+ private:
+ CRITICAL_SECTION* get_critsec()
+ {
+ static CRITICAL_SECTION g_critsec;
+ return &g_critsec;
+ }
+ };
+
+ class multi_threaded_local
+ {
+ public:
+ multi_threaded_local()
+ {
+ InitializeCriticalSection(&m_critsec);
+ }
+
+ multi_threaded_local(const multi_threaded_local&)
+ {
+ InitializeCriticalSection(&m_critsec);
+ }
+
+ virtual ~multi_threaded_local()
+ {
+ DeleteCriticalSection(&m_critsec);
+ }
+
+ virtual void lock()
+ {
+ EnterCriticalSection(&m_critsec);
+ }
+
+ virtual void unlock()
+ {
+ LeaveCriticalSection(&m_critsec);
+ }
+
+ private:
+ CRITICAL_SECTION m_critsec;
+ };
+# endif // _SIGSLOT_HAS_WIN32_THREADS
+
+# ifdef _SIGSLOT_HAS_POSIX_THREADS
+ // The multi threading policies only get compiled in if they are enabled.
+ class multi_threaded_global
+ {
+ public:
+ multi_threaded_global()
+ {
+ pthread_mutex_init(get_mutex(), NULL);
+ }
+
+ multi_threaded_global(const multi_threaded_global&)
+ {
+ ;
+ }
+
+ virtual ~multi_threaded_global()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ pthread_mutex_lock(get_mutex());
+ }
+
+ virtual void unlock()
+ {
+ pthread_mutex_unlock(get_mutex());
+ }
+
+ private:
+ pthread_mutex_t* get_mutex()
+ {
+ static pthread_mutex_t g_mutex;
+ return &g_mutex;
+ }
+ };
+
+ class multi_threaded_local
+ {
+ public:
+ multi_threaded_local()
+ {
+ pthread_mutex_init(&m_mutex, NULL);
+ }
+
+ multi_threaded_local(const multi_threaded_local&)
+ {
+ pthread_mutex_init(&m_mutex, NULL);
+ }
+
+ virtual ~multi_threaded_local()
+ {
+ pthread_mutex_destroy(&m_mutex);
+ }
+
+ virtual void lock()
+ {
+ pthread_mutex_lock(&m_mutex);
+ }
+
+ virtual void unlock()
+ {
+ pthread_mutex_unlock(&m_mutex);
+ }
+
+ private:
+ pthread_mutex_t m_mutex;
+ };
+# endif // _SIGSLOT_HAS_POSIX_THREADS
+#endif // SIGSLOT_LEAVE_OUT_MULTITHREADING
+
+ template<class mt_policy>
+ class lock_block
+ {
+ public:
+ mt_policy *m_mutex;
+
+ explicit lock_block(mt_policy *mtx)
+ : m_mutex(mtx)
+ {
+ m_mutex->lock();
+ }
+
+ ~lock_block()
+ {
+ m_mutex->unlock();
+ }
+ };
+
+ class has_slots_interface;
+
+ template<class mt_policy>
+ class _connection_base0
+ {
+ public:
+ virtual ~_connection_base0() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit() = 0;
+ virtual _connection_base0* clone() = 0;
+ virtual _connection_base0* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class mt_policy>
+ class _connection_base1
+ {
+ public:
+ virtual ~_connection_base1() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type) = 0;
+ virtual _connection_base1<arg1_type, mt_policy>* clone() = 0;
+ virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy>
+ class _connection_base2
+ {
+ public:
+ virtual ~_connection_base2() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type) = 0;
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() = 0;
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _connection_base3
+ {
+ public:
+ virtual ~_connection_base3() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type) = 0;
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() = 0;
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+ class _connection_base4
+ {
+ public:
+ virtual ~_connection_base4() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0;
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() = 0;
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy>
+ class _connection_base5
+ {
+ public:
+ virtual ~_connection_base5() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type) = 0;
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* clone() = 0;
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy>
+ class _connection_base6
+ {
+ public:
+ virtual ~_connection_base6() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type) = 0;
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* clone() = 0;
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _connection_base7
+ {
+ public:
+ virtual ~_connection_base7() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type, arg7_type) = 0;
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* clone() = 0;
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+ class _connection_base8
+ {
+ public:
+ virtual ~_connection_base8() {}
+ virtual has_slots_interface* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type, arg7_type, arg8_type) = 0;
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() = 0;
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots_interface* pnewdest) = 0;
+ };
+
+ class _signal_base_interface
+ {
+ public:
+ virtual void slot_disconnect(has_slots_interface* pslot) = 0;
+ virtual void slot_duplicate(const has_slots_interface* poldslot, has_slots_interface* pnewslot) = 0;
+ };
+
+ template<class mt_policy>
+ class _signal_base : public _signal_base_interface, public mt_policy
+ {
+ };
+
+ class has_slots_interface
+ {
+ public:
+ has_slots_interface()
+ {
+ ;
+ }
+
+ virtual void signal_connect(_signal_base_interface* sender) = 0;
+
+ virtual void signal_disconnect(_signal_base_interface* sender) = 0;
+
+ virtual ~has_slots_interface()
+ {
+ }
+
+ virtual void disconnect_all() = 0;
+ };
+
+ template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class has_slots : public has_slots_interface, public mt_policy
+ {
+ private:
+ typedef std::set<_signal_base_interface*> sender_set;
+ typedef sender_set::const_iterator const_iterator;
+
+ public:
+ has_slots()
+ {
+ ;
+ }
+
+ has_slots(const has_slots& hs)
+ {
+ lock_block<mt_policy> lock(this);
+ const_iterator it = hs.m_senders.begin();
+ const_iterator itEnd = hs.m_senders.end();
+
+ while(it != itEnd)
+ {
+ (*it)->slot_duplicate(&hs, this);
+ m_senders.insert(*it);
+ ++it;
+ }
+ }
+
+ void signal_connect(_signal_base_interface* sender)
+ {
+ lock_block<mt_policy> lock(this);
+ m_senders.insert(sender);
+ }
+
+ void signal_disconnect(_signal_base_interface* sender)
+ {
+ lock_block<mt_policy> lock(this);
+ m_senders.erase(sender);
+ }
+
+ virtual ~has_slots()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ const_iterator it = m_senders.begin();
+ const_iterator itEnd = m_senders.end();
+
+ while(it != itEnd)
+ {
+ (*it)->slot_disconnect(this);
+ ++it;
+ }
+
+ m_senders.erase(m_senders.begin(), m_senders.end());
+ }
+
+ private:
+ sender_set m_senders;
+ };
+
+ template<class mt_policy>
+ class _signal_base0 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base0<mt_policy> *> connections_list;
+
+ _signal_base0()
+ {
+ ;
+ }
+
+ _signal_base0(const _signal_base0& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ ~_signal_base0()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class mt_policy>
+ class _signal_base1 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base1<arg1_type, mt_policy> *> connections_list;
+
+ _signal_base1()
+ {
+ ;
+ }
+
+ _signal_base1(const _signal_base1<arg1_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base1()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy>
+ class _signal_base2 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base2<arg1_type, arg2_type, mt_policy> *>
+ connections_list;
+
+ _signal_base2()
+ {
+ ;
+ }
+
+ _signal_base2(const _signal_base2<arg1_type, arg2_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base2()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _signal_base3 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> *>
+ connections_list;
+
+ _signal_base3()
+ {
+ ;
+ }
+
+ _signal_base3(const _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base3()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+ class _signal_base4 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base4<arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy> *> connections_list;
+
+ _signal_base4()
+ {
+ ;
+ }
+
+ _signal_base4(const _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base4()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy>
+ class _signal_base5 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base5<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, mt_policy> *> connections_list;
+
+ _signal_base5()
+ {
+ ;
+ }
+
+ _signal_base5(const _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base5()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy>
+ class _signal_base6 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base6<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy> *> connections_list;
+
+ _signal_base6()
+ {
+ ;
+ }
+
+ _signal_base6(const _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base6()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _signal_base7 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base7<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> *> connections_list;
+
+ _signal_base7()
+ {
+ ;
+ }
+
+ _signal_base7(const _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base7()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+ class _signal_base8 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base8<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> *>
+ connections_list;
+
+ _signal_base8()
+ {
+ ;
+ }
+
+ _signal_base8(const _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots_interface* oldtarget, has_slots_interface* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base8()
+ {
+ disconnect_all();
+ }
+
+ bool is_empty()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ return it == itEnd;
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots_interface* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+
+ template<class dest_type, class mt_policy>
+ class _connection0 : public _connection_base0<mt_policy>
+ {
+ public:
+ _connection0()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection0(dest_type* pobject, void (dest_type::*pmemfun)())
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection0()
+ {
+ }
+
+ virtual _connection_base0<mt_policy>* clone()
+ {
+ return new _connection0<dest_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base0<mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection0<dest_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit()
+ {
+ (m_pobject->*m_pmemfun)();
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)();
+ };
+
+ template<class dest_type, class arg1_type, class mt_policy>
+ class _connection1 : public _connection_base1<arg1_type, mt_policy>
+ {
+ public:
+ _connection1()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection1()
+ {
+ }
+
+ virtual _connection_base1<arg1_type, mt_policy>* clone()
+ {
+ return new _connection1<dest_type, arg1_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection1<dest_type, arg1_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1)
+ {
+ (m_pobject->*m_pmemfun)(a1);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class mt_policy>
+ class _connection2 : public _connection_base2<arg1_type, arg2_type, mt_policy>
+ {
+ public:
+ _connection2()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection2()
+ {
+ }
+
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone()
+ {
+ return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _connection3 : public _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+ {
+ public:
+ _connection3()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection3()
+ {
+ }
+
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone()
+ {
+ return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class mt_policy>
+ class _connection4 : public _connection_base4<arg1_type, arg2_type,
+ arg3_type, arg4_type, mt_policy>
+ {
+ public:
+ _connection4()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection4()
+ {
+ }
+
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone()
+ {
+ return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3,
+ arg4_type a4)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type,
+ arg4_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class mt_policy>
+ class _connection5 : public _connection_base5<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, mt_policy>
+ {
+ public:
+ _connection5()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection5()
+ {
+ }
+
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* clone()
+ {
+ return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class mt_policy>
+ class _connection6 : public _connection_base6<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, mt_policy>
+ {
+ public:
+ _connection6()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection6()
+ {
+ }
+
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* clone()
+ {
+ return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _connection7 : public _connection_base7<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+ {
+ public:
+ _connection7()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection7()
+ {
+ }
+
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* clone()
+ {
+ return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class arg7_type,
+ class arg8_type, class mt_policy>
+ class _connection8 : public _connection_base8<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+ {
+ public:
+ _connection8()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type, arg8_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual ~_connection8()
+ {
+ }
+
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone()
+ {
+ return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots_interface* pnewdest)
+ {
+ return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8);
+ }
+
+ virtual has_slots_interface* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type);
+ };
+
+ template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal0 : public _signal_base0<mt_policy>
+ {
+ public:
+ typedef _signal_base0<mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal0()
+ {
+ ;
+ }
+
+ signal0(const signal0<mt_policy>& s)
+ : _signal_base0<mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)())
+ {
+ lock_block<mt_policy> lock(this);
+ _connection0<desttype, mt_policy>* conn =
+ new _connection0<desttype, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit();
+
+ it = itNext;
+ }
+ }
+
+ void operator()()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit();
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal1 : public _signal_base1<arg1_type, mt_policy>
+ {
+ public:
+ typedef _signal_base1<arg1_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal1()
+ {
+ ;
+ }
+
+ signal1(const signal1<arg1_type, mt_policy>& s)
+ : _signal_base1<arg1_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection1<desttype, arg1_type, mt_policy>* conn =
+ new _connection1<desttype, arg1_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal2 : public _signal_base2<arg1_type, arg2_type, mt_policy>
+ {
+ public:
+ typedef _signal_base2<arg1_type, arg2_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal2()
+ {
+ ;
+ }
+
+ signal2(const signal2<arg1_type, arg2_type, mt_policy>& s)
+ : _signal_base2<arg1_type, arg2_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection2<desttype, arg1_type, arg2_type, mt_policy>* conn = new
+ _connection2<desttype, arg1_type, arg2_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal3 : public _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+ {
+ public:
+ typedef _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal3()
+ {
+ ;
+ }
+
+ signal3(const signal3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+ : _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>* conn =
+ new _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>(pclass,
+ pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal4 : public _signal_base4<arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy>
+ {
+ public:
+ typedef _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal4()
+ {
+ ;
+ }
+
+ signal4(const signal4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+ : _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection4<desttype, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>*
+ conn = new _connection4<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal5 : public _signal_base5<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, mt_policy>
+ {
+ public:
+ typedef _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal5()
+ {
+ ;
+ }
+
+ signal5(const signal5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>& s)
+ : _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection5<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* conn = new _connection5<desttype, arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5);
+
+ it = itNext;
+ }
+ }
+ };
+
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal6 : public _signal_base6<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy>
+ {
+ public:
+ typedef _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal6()
+ {
+ ;
+ }
+
+ signal6(const signal6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>& s)
+ : _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection6<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* conn =
+ new _connection6<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal7 : public _signal_base7<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+ {
+ public:
+ typedef _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal7()
+ {
+ ;
+ }
+
+ signal7(const signal7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>& s)
+ : _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection7<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* conn =
+ new _connection7<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal8 : public _signal_base8<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+ {
+ public:
+ typedef _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal8()
+ {
+ ;
+ }
+
+ signal8(const signal8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+ : _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type, arg8_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection8<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* conn =
+ new _connection8<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type,
+ arg8_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+ it = itNext;
+ }
+ }
+ };
+
+} // namespace sigslot
+
+#endif // TALK_BASE_SIGSLOT_H__
diff --git a/media/mtransport/simpletokenbucket.cpp b/media/mtransport/simpletokenbucket.cpp
new file mode 100644
index 000000000..6145a2bbf
--- /dev/null
+++ b/media/mtransport/simpletokenbucket.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Original author: bcampen@mozilla.com */
+
+#include "simpletokenbucket.h"
+
+#include <stdint.h>
+
+#include "prinrval.h"
+
+namespace mozilla {
+
+SimpleTokenBucket::SimpleTokenBucket(size_t bucket_size,
+ size_t tokens_per_second) :
+ max_tokens_(bucket_size),
+ num_tokens_(bucket_size),
+ tokens_per_second_(tokens_per_second),
+ last_time_tokens_added_(PR_IntervalNow()) {
+}
+
+size_t SimpleTokenBucket::getTokens(size_t num_requested_tokens) {
+ // Only fill if there isn't enough to satisfy the request.
+ // If we get tokens so seldomly that we are able to roll the timer all
+ // the way around its range, then we lose that entire range of time
+ // for token accumulation. Probably not the end of the world.
+ if (num_requested_tokens > num_tokens_) {
+ PRIntervalTime now = PR_IntervalNow();
+
+ // If we roll over the max, since everything in this calculation is the same
+ // unsigned type, this will still yield the elapsed time (unless we've
+ // wrapped more than once).
+ PRIntervalTime elapsed_ticks = now - last_time_tokens_added_;
+
+ uint32_t elapsed_milli_sec = PR_IntervalToMilliseconds(elapsed_ticks);
+ size_t tokens_to_add = (elapsed_milli_sec * tokens_per_second_)/1000;
+
+ // Only update our timestamp if we added some tokens
+ // TODO:(bcampen@mozilla.com) Should we attempt to "save" leftover time?
+ if (tokens_to_add) {
+ num_tokens_ += tokens_to_add;
+ if (num_tokens_ > max_tokens_) {
+ num_tokens_ = max_tokens_;
+ }
+
+ last_time_tokens_added_ = now;
+ }
+
+ if (num_requested_tokens > num_tokens_) {
+ return num_tokens_;
+ }
+ }
+
+ num_tokens_ -= num_requested_tokens;
+ return num_requested_tokens;
+}
+
+} // namespace mozilla
+
diff --git a/media/mtransport/simpletokenbucket.h b/media/mtransport/simpletokenbucket.h
new file mode 100644
index 000000000..ee94825cb
--- /dev/null
+++ b/media/mtransport/simpletokenbucket.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Original author: bcampen@mozilla.com */
+
+/*
+ * This file defines a dirt-simple token bucket class.
+ */
+
+#ifndef simpletokenbucket_h__
+#define simpletokenbucket_h__
+
+#include <stdint.h>
+
+#include "prinrval.h"
+
+#include "m_cpp_utils.h"
+
+namespace mozilla {
+
+class SimpleTokenBucket {
+ public:
+ /*
+ * Create a SimpleTokenBucket with a given maximum size and
+ * token replenishment rate.
+ * (eg; if you want a maximum rate of 5 per second over a 7 second
+ * period, call SimpleTokenBucket b(5*7, 5);)
+ */
+ SimpleTokenBucket(size_t bucket_size, size_t tokens_per_second);
+
+ /*
+ * Attempt to acquire a number of tokens. If successful, returns
+ * |num_tokens|, otherwise returns the number of tokens currently
+ * in the bucket.
+ * Note: To get the number of tokens in the bucket, pass something
+ * like UINT32_MAX.
+ */
+ size_t getTokens(size_t num_tokens);
+
+ protected: // Allow testing to touch these.
+ uint64_t max_tokens_;
+ uint64_t num_tokens_;
+ size_t tokens_per_second_;
+ PRIntervalTime last_time_tokens_added_;
+
+ DISALLOW_COPY_ASSIGN(SimpleTokenBucket);
+};
+
+} // namespace mozilla
+
+#endif // simpletokenbucket_h__
+
diff --git a/media/mtransport/stun_socket_filter.cpp b/media/mtransport/stun_socket_filter.cpp
new file mode 100644
index 000000000..5e7676442
--- /dev/null
+++ b/media/mtransport/stun_socket_filter.cpp
@@ -0,0 +1,359 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <string>
+#include <set>
+
+extern "C" {
+#include "nr_api.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "mozilla/Attributes.h"
+#include "mozilla/net/DNS.h"
+#include "stun_socket_filter.h"
+#include "nr_socket_prsock.h"
+
+namespace {
+
+class NetAddrCompare {
+ public:
+ bool operator()(const mozilla::net::NetAddr& lhs,
+ const mozilla::net::NetAddr& rhs) const {
+ if (lhs.raw.family != rhs.raw.family) {
+ return lhs.raw.family < rhs.raw.family;
+ }
+
+ switch (lhs.raw.family) {
+ case AF_INET:
+ if (lhs.inet.port != rhs.inet.port) {
+ return lhs.inet.port < rhs.inet.port;
+ }
+ return lhs.inet.ip < rhs.inet.ip;
+ case AF_INET6:
+ if (lhs.inet6.port != rhs.inet6.port) {
+ return lhs.inet6.port < rhs.inet6.port;
+ }
+ return memcmp(&lhs.inet6.ip, &rhs.inet6.ip, sizeof(lhs.inet6.ip)) < 0;
+ default:
+ MOZ_ASSERT(false);
+ }
+ return false;
+ }
+};
+
+class PendingSTUNRequest {
+ public:
+ PendingSTUNRequest(const mozilla::net::NetAddr& netaddr, const UINT12 &id)
+ : id_(id),
+ net_addr_(netaddr),
+ is_id_set_(true) {}
+
+ MOZ_IMPLICIT PendingSTUNRequest(const mozilla::net::NetAddr& netaddr)
+ : id_(),
+ net_addr_(netaddr),
+ is_id_set_(false) {}
+
+ bool operator<(const PendingSTUNRequest& rhs) const {
+ if (NetAddrCompare()(net_addr_, rhs.net_addr_)) {
+ return true;
+ }
+
+ if (NetAddrCompare()(rhs.net_addr_, net_addr_)) {
+ return false;
+ }
+
+ if (!is_id_set_ && !rhs.is_id_set_) {
+ // PendingSTUNRequest can be stored to set only when it has id,
+ // so comparing two PendingSTUNRequst without id is not going
+ // to happen.
+ MOZ_CRASH();
+ }
+
+ if (!(is_id_set_ && rhs.is_id_set_)) {
+ // one of operands doesn't have id, ignore the difference.
+ return false;
+ }
+
+ return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0;
+ }
+
+ private:
+ const UINT12 id_;
+ const mozilla::net::NetAddr net_addr_;
+ const bool is_id_set_;
+};
+
+class STUNUDPSocketFilter : public nsISocketFilter {
+ public:
+ STUNUDPSocketFilter()
+ : white_list_(),
+ pending_requests_() {}
+
+ // Allocated/freed and used on the PBackground IPC thread
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISOCKETFILTER
+
+ private:
+ virtual ~STUNUDPSocketFilter() {}
+
+ bool filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
+ const uint8_t *data,
+ uint32_t len);
+
+ bool filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr,
+ const uint8_t *data,
+ uint32_t len);
+
+ std::set<mozilla::net::NetAddr, NetAddrCompare> white_list_;
+ std::set<PendingSTUNRequest> pending_requests_;
+ std::set<PendingSTUNRequest> response_allowed_;
+};
+
+NS_IMPL_ISUPPORTS(STUNUDPSocketFilter, nsISocketFilter)
+
+NS_IMETHODIMP
+STUNUDPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr,
+ const uint8_t *data,
+ uint32_t len,
+ int32_t direction,
+ bool *result) {
+ switch (direction) {
+ case nsISocketFilter::SF_INCOMING:
+ *result = filter_incoming_packet(remote_addr, data, len);
+ break;
+ case nsISocketFilter::SF_OUTGOING:
+ *result = filter_outgoing_packet(remote_addr, data, len);
+ break;
+ default:
+ MOZ_CRASH("Unknown packet direction");
+ }
+ return NS_OK;
+}
+
+bool STUNUDPSocketFilter::filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
+ const uint8_t *data, uint32_t len) {
+ // Check white list
+ if (white_list_.find(*remote_addr) != white_list_.end()) {
+ return true;
+ }
+
+ // Check if we had sent any stun request to this destination. If we had sent a request
+ // to this host, we check the transaction id, and we can add this address to whitelist.
+ std::set<PendingSTUNRequest>::iterator it =
+ pending_requests_.find(PendingSTUNRequest(*remote_addr));
+ if (it != pending_requests_.end()) {
+ if (nr_is_stun_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+ const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
+ // If it is a STUN response message and we can match its id with one of the pending
+ // requests, we can add this address into whitelist.
+ if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+ PendingSTUNRequest pending_req(*remote_addr, msg->id);
+ std::set<PendingSTUNRequest>::iterator it = pending_requests_.find(pending_req);
+ if (it != pending_requests_.end()) {
+ pending_requests_.erase(it);
+ response_allowed_.erase(pending_req);
+ white_list_.insert(*remote_addr);
+ }
+ } else {
+ // If it is a STUN message, but not a response message, we add it into response
+ // allowed list and allow outgoing filter to send a response back.
+ response_allowed_.insert(PendingSTUNRequest(*remote_addr, msg->id));
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool STUNUDPSocketFilter::filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr,
+ const uint8_t *data, uint32_t len) {
+ // Check white list
+ if (white_list_.find(*remote_addr) != white_list_.end()) {
+ return true;
+ }
+
+ // Check if it is a stun packet. If yes, we put it into a pending list and wait for
+ // response packet.
+ if (nr_is_stun_request_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+ const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
+ pending_requests_.insert(PendingSTUNRequest(*remote_addr, msg->id));
+ return true;
+ }
+
+ // If it is a stun response packet, and we had received the request before, we can
+ // allow it packet to pass filter.
+ if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+ const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
+ std::set<PendingSTUNRequest>::iterator it =
+ response_allowed_.find(PendingSTUNRequest(*remote_addr, msg->id));
+ if (it != response_allowed_.end()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+class PendingSTUNId {
+ public:
+ explicit PendingSTUNId(const UINT12 &id)
+ : id_(id) {}
+
+ bool operator<(const PendingSTUNId& rhs) const {
+ return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0;
+ }
+ private:
+ const UINT12 id_;
+};
+
+class STUNTCPSocketFilter : public nsISocketFilter {
+ public:
+ STUNTCPSocketFilter()
+ : white_listed_(false),
+ pending_request_ids_(),
+ response_allowed_ids_() {}
+
+ // Allocated/freed and used on the PBackground IPC thread
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISOCKETFILTER
+
+ private:
+ virtual ~STUNTCPSocketFilter() {}
+
+ bool filter_incoming_packet(const uint8_t *data,
+ uint32_t len);
+
+ bool filter_outgoing_packet(const uint8_t *data,
+ uint32_t len);
+
+ bool white_listed_;
+ std::set<PendingSTUNId> pending_request_ids_;
+ std::set<PendingSTUNId> response_allowed_ids_;
+};
+
+NS_IMPL_ISUPPORTS(STUNTCPSocketFilter, nsISocketFilter)
+
+NS_IMETHODIMP
+STUNTCPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr,
+ const uint8_t *data,
+ uint32_t len,
+ int32_t direction,
+ bool *result) {
+ switch (direction) {
+ case nsISocketFilter::SF_INCOMING:
+ *result = filter_incoming_packet(data, len);
+ break;
+ case nsISocketFilter::SF_OUTGOING:
+ *result = filter_outgoing_packet(data, len);
+ break;
+ default:
+ MOZ_CRASH("Unknown packet direction");
+ }
+ return NS_OK;
+}
+
+bool STUNTCPSocketFilter::filter_incoming_packet(const uint8_t *data, uint32_t len) {
+ // check if white listed already
+ if (white_listed_) {
+ return true;
+ }
+
+ UCHAR* stun = const_cast<uint8_t*>(data);
+ uint32_t length = len;
+ if (!nr_is_stun_message(stun, length)) {
+ stun += 2;
+ length -= 2;
+ if (!nr_is_stun_message(stun, length)) {
+ // Note: the UDP filter lets incoming packets pass, because order of
+ // packets is not guaranteed and the next packet is likely an important
+ // packet for DTLS (which is costly in terms of timing to wait for a
+ // retransmit). This does not apply to TCP with its guaranteed order. But
+ // we still let it pass, because otherwise we would have to buffer bytes
+ // here until the minimum STUN request size of bytes has been received.
+ return true;
+ }
+ }
+
+ const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(stun);
+
+ // If it is a STUN response message and we can match its id with one of the
+ // pending requests, we can add this address into whitelist.
+ if (nr_is_stun_response_message(stun, length)) {
+ std::set<PendingSTUNId>::iterator it =
+ pending_request_ids_.find(PendingSTUNId(msg->id));
+ if (it != pending_request_ids_.end()) {
+ pending_request_ids_.erase(it);
+ white_listed_ = true;
+ }
+ } else {
+ // If it is a STUN message, but not a response message, we add it into
+ // response allowed list and allow outgoing filter to send a response back.
+ response_allowed_ids_.insert(PendingSTUNId(msg->id));
+ }
+
+ return true;
+}
+
+bool STUNTCPSocketFilter::filter_outgoing_packet(const uint8_t *data, uint32_t len) {
+ // check if white listed already
+ if (white_listed_) {
+ return true;
+ }
+
+ UCHAR* stun = const_cast<uint8_t*>(data);
+ uint32_t length = len;
+ if (!nr_is_stun_message(stun, length)) {
+ stun += 2;
+ length -= 2;
+ if (!nr_is_stun_message(stun, length)) {
+ return false;
+ }
+ }
+
+ const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(stun);
+
+ // Check if it is a stun request. If yes, we put it into a pending list and wait for
+ // response packet.
+ if (nr_is_stun_request_message(stun, length)) {
+ pending_request_ids_.insert(PendingSTUNId(msg->id));
+ return true;
+ }
+
+ // If it is a stun response packet, and we had received the request before, we can
+ // allow it packet to pass filter.
+ if (nr_is_stun_response_message(stun, length)) {
+ std::set<PendingSTUNId>::iterator it =
+ response_allowed_ids_.find(PendingSTUNId(msg->id));
+ if (it != response_allowed_ids_.end()) {
+ response_allowed_ids_.erase(it);
+ white_listed_ = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+NS_IMPL_ISUPPORTS(nsStunUDPSocketFilterHandler, nsISocketFilterHandler)
+
+NS_IMETHODIMP nsStunUDPSocketFilterHandler::NewFilter(nsISocketFilter **result)
+{
+ nsISocketFilter *ret = new STUNUDPSocketFilter();
+ NS_ADDREF(*result = ret);
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsStunTCPSocketFilterHandler, nsISocketFilterHandler)
+
+NS_IMETHODIMP nsStunTCPSocketFilterHandler::NewFilter(nsISocketFilter **result)
+{
+ nsISocketFilter *ret = new STUNTCPSocketFilter();
+ NS_ADDREF(*result = ret);
+ return NS_OK;
+}
+
diff --git a/media/mtransport/stun_socket_filter.h b/media/mtransport/stun_socket_filter.h
new file mode 100644
index 000000000..737a4e833
--- /dev/null
+++ b/media/mtransport/stun_socket_filter.h
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef stun_socket_filter_h__
+#define stun_socket_filter_h__
+
+#include "nsISocketFilter.h"
+
+#define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID { 0x3e43ee93, 0x829e, 0x4ea6, \
+ { 0xa3, 0x4e, 0x62, 0xd9, 0xe4, 0xc9, 0xf9, 0x93 } };
+
+class nsStunUDPSocketFilterHandler : public nsISocketFilterHandler {
+public:
+ // Threadsafe because we create off-main-thread, but destroy on MainThread
+ // via FreeFactoryEntries()
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISOCKETFILTERHANDLER
+private:
+ virtual ~nsStunUDPSocketFilterHandler() {}
+};
+
+#define NS_STUN_TCP_SOCKET_FILTER_HANDLER_CID { 0x9fea635a, 0x2fc2, 0x4d08, \
+ { 0x97, 0x21, 0xd2, 0x38, 0xd3, 0xf5, 0x2f, 0x92 } };
+
+class nsStunTCPSocketFilterHandler : public nsISocketFilterHandler {
+public:
+ // Threadsafe because we create off-main-thread, but destroy on MainThread
+ // via FreeFactoryEntries()
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISOCKETFILTERHANDLER
+private:
+ virtual ~nsStunTCPSocketFilterHandler() {}
+};
+
+
+#endif // stun_socket_filter_h__
diff --git a/media/mtransport/test/TestSyncRunnable.cpp b/media/mtransport/test/TestSyncRunnable.cpp
new file mode 100644
index 000000000..02c7d5120
--- /dev/null
+++ b/media/mtransport/test/TestSyncRunnable.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 12; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsThreadUtils.h"
+#include "mozilla/SyncRunnable.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+nsIThread *gThread = nullptr;
+
+class TestRunnable : public Runnable {
+public:
+ TestRunnable() : ran_(false) {}
+
+ NS_IMETHOD Run() override
+ {
+ ran_ = true;
+
+ return NS_OK;
+ }
+
+ bool ran() const { return ran_; }
+
+private:
+ bool ran_;
+};
+
+class TestSyncRunnable : public ::testing::Test {
+public:
+ static void SetUpTestCase()
+ {
+ nsresult rv = NS_NewNamedThread("thread", &gThread);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ }
+
+ static void TearDownTestCase()
+ {
+ if (gThread)
+ gThread->Shutdown();
+ }
+};
+
+TEST_F(TestSyncRunnable, TestDispatch)
+{
+ RefPtr<TestRunnable> r(new TestRunnable());
+ RefPtr<SyncRunnable> s(new SyncRunnable(r));
+ s->DispatchToThread(gThread);
+
+ ASSERT_TRUE(r->ran());
+}
+
+TEST_F(TestSyncRunnable, TestDispatchStatic)
+{
+ RefPtr<TestRunnable> r(new TestRunnable());
+ SyncRunnable::DispatchToThread(gThread, r);
+ ASSERT_TRUE(r->ran());
+}
diff --git a/media/mtransport/test/buffered_stun_socket_unittest.cpp b/media/mtransport/test/buffered_stun_socket_unittest.cpp
new file mode 100644
index 000000000..32565215c
--- /dev/null
+++ b/media/mtransport/test/buffered_stun_socket_unittest.cpp
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include <iostream>
+
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "nr_socket_buffered_stun.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "databuffer.h"
+#include "dummysocket.h"
+
+#include "nr_socket_prsock.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+static uint8_t kStunMessage[] = {
+ 0x00, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42,
+ 0x9b, 0x90, 0xbe, 0x2c, 0xae, 0x1a, 0x0c, 0xa8,
+ 0xa0, 0xd6, 0x8b, 0x08, 0x80, 0x28, 0x00, 0x04,
+ 0xdb, 0x35, 0x5f, 0xaa
+};
+static size_t kStunMessageLen = sizeof(kStunMessage);
+
+class BufferedStunSocketTest : public MtransportTest {
+ public:
+ BufferedStunSocketTest()
+ : MtransportTest(),
+ dummy_(nullptr),
+ test_socket_(nullptr) { }
+
+ ~BufferedStunSocketTest() {
+ nr_socket_destroy(&test_socket_);
+ }
+
+ void SetUp() {
+ MtransportTest::SetUp();
+
+ RefPtr<DummySocket> dummy(new DummySocket());
+
+ int r = nr_socket_buffered_stun_create(
+ dummy->get_nr_socket(),
+ kStunMessageLen,
+ TURN_TCP_FRAMING,
+ &test_socket_);
+ ASSERT_EQ(0, r);
+ dummy_ = dummy.forget(); // Now owned by test_socket_.
+
+ r = nr_str_port_to_transport_addr(
+ (char *)"192.0.2.133", 3333, IPPROTO_TCP, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ r = nr_socket_connect(test_socket_,
+ &remote_addr_);
+ ASSERT_EQ(0, r);
+ }
+
+ nr_socket *socket() { return test_socket_; }
+
+ protected:
+ RefPtr<DummySocket> dummy_;
+ nr_socket *test_socket_;
+ nr_transport_addr remote_addr_;
+};
+
+
+TEST_F(BufferedStunSocketTest, TestCreate) {
+}
+
+TEST_F(BufferedStunSocketTest, TestSendTo) {
+ int r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToBuffered) {
+ dummy_->SetWritable(0);
+
+ int r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ dummy_->CheckWriteBuffer(nullptr, 0);
+
+ dummy_->SetWritable(kStunMessageLen);
+ dummy_->FireWritableCb();
+ dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendFullThenDrain) {
+ dummy_->SetWritable(0);
+
+ for (;;) {
+ int r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &remote_addr_);
+ if (r == R_WOULDBLOCK)
+ break;
+
+ ASSERT_EQ(0, r);
+ }
+
+ // Nothing was written.
+ dummy_->CheckWriteBuffer(nullptr, 0);
+
+ // Now flush.
+ dummy_->SetWritable(kStunMessageLen);
+ dummy_->FireWritableCb();
+ dummy_->ClearWriteBuffer();
+
+ // Verify we can write something.
+ int r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ // And that it appears.
+ dummy_->CheckWriteBuffer(kStunMessage, kStunMessageLen);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToPartialBuffered) {
+ dummy_->SetWritable(10);
+
+ int r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ dummy_->CheckWriteBuffer(kStunMessage, 10);
+ dummy_->ClearWriteBuffer();
+
+ dummy_->SetWritable(kStunMessageLen);
+ dummy_->FireWritableCb();
+ dummy_->CheckWriteBuffer(kStunMessage + 10, kStunMessageLen - 10);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToReject) {
+ dummy_->SetWritable(0);
+
+ int r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ dummy_->CheckWriteBuffer(nullptr, 0);
+
+ r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &remote_addr_);
+ ASSERT_EQ(R_WOULDBLOCK, r);
+
+ dummy_->CheckWriteBuffer(nullptr, 0);
+}
+
+TEST_F(BufferedStunSocketTest, TestSendToWrongAddr) {
+ nr_transport_addr addr;
+
+ int r = nr_str_port_to_transport_addr(
+ (char *)"192.0.2.134", 3333, IPPROTO_TCP, &addr);
+ ASSERT_EQ(0, r);
+
+ r = nr_socket_sendto(test_socket_,
+ kStunMessage,
+ kStunMessageLen,
+ 0, &addr);
+ ASSERT_EQ(R_BAD_DATA, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFrom) {
+ dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
+
+ unsigned char tmp[2048];
+ size_t len;
+ nr_transport_addr addr;
+
+ int r = nr_socket_recvfrom(test_socket_,
+ tmp, sizeof(tmp), &len, 0,
+ &addr);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(kStunMessageLen, len);
+ ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
+ ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromPartial) {
+ dummy_->SetReadBuffer(kStunMessage, 15);
+
+ unsigned char tmp[2048];
+ size_t len;
+ nr_transport_addr addr;
+
+ int r = nr_socket_recvfrom(test_socket_,
+ tmp, sizeof(tmp), &len, 0,
+ &addr);
+ ASSERT_EQ(R_WOULDBLOCK, r);
+
+
+ dummy_->SetReadBuffer(kStunMessage + 15, kStunMessageLen - 15);
+
+ r = nr_socket_recvfrom(test_socket_,
+ tmp, sizeof(tmp), &len, 0,
+ &addr);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(kStunMessageLen, len);
+ ASSERT_EQ(0, memcmp(kStunMessage, tmp, kStunMessageLen));
+ ASSERT_EQ(0, nr_transport_addr_cmp(&addr, &remote_addr_,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+
+ r = nr_socket_recvfrom(test_socket_,
+ tmp, sizeof(tmp), &len, 0,
+ &addr);
+ ASSERT_EQ(R_WOULDBLOCK, r);
+}
+
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromGarbage) {
+ uint8_t garbage[50];
+ memset(garbage, 0xff, sizeof(garbage));
+
+ dummy_->SetReadBuffer(garbage, sizeof(garbage));
+
+ unsigned char tmp[2048];
+ size_t len;
+ nr_transport_addr addr;
+ int r = nr_socket_recvfrom(test_socket_,
+ tmp, sizeof(tmp), &len, 0,
+ &addr);
+ ASSERT_EQ(R_BAD_DATA, r);
+
+ r = nr_socket_recvfrom(test_socket_,
+ tmp, sizeof(tmp), &len, 0,
+ &addr);
+ ASSERT_EQ(R_FAILED, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromTooShort) {
+ dummy_->SetReadBuffer(kStunMessage, kStunMessageLen);
+
+ unsigned char tmp[2048];
+ size_t len;
+ nr_transport_addr addr;
+
+ int r = nr_socket_recvfrom(test_socket_,
+ tmp, kStunMessageLen - 1, &len, 0,
+ &addr);
+ ASSERT_EQ(R_BAD_ARGS, r);
+}
+
+TEST_F(BufferedStunSocketTest, TestReceiveRecvFromReallyLong) {
+ uint8_t garbage[4096];
+ memset(garbage, 0xff, sizeof(garbage));
+ memcpy(garbage, kStunMessage, kStunMessageLen);
+ nr_stun_message_header *hdr = reinterpret_cast<nr_stun_message_header *>
+ (garbage);
+ hdr->length = htons(3000);
+
+ dummy_->SetReadBuffer(garbage, sizeof(garbage));
+
+ unsigned char tmp[4096];
+ size_t len;
+ nr_transport_addr addr;
+
+ int r = nr_socket_recvfrom(test_socket_,
+ tmp, kStunMessageLen - 1, &len, 0,
+ &addr);
+ ASSERT_EQ(R_BAD_DATA, r);
+}
diff --git a/media/mtransport/test/dummysocket.h b/media/mtransport/test/dummysocket.h
new file mode 100644
index 000000000..a5d138940
--- /dev/null
+++ b/media/mtransport/test/dummysocket.h
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original authors: ekr@rtfm.com; ryan@tokbox.com
+
+#ifndef MTRANSPORT_DUMMY_SOCKET_H_
+#define MTRANSPORT_DUMMY_SOCKET_H_
+
+#include "nr_socket_prsock.h"
+
+extern "C" {
+#include "transport_addr.h"
+}
+
+#include "databuffer.h"
+#include "mozilla/UniquePtr.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+namespace mozilla {
+
+static UniquePtr<DataBuffer> merge(UniquePtr<DataBuffer> a, UniquePtr<DataBuffer> b) {
+ if (a && a->len() && b && b->len()) {
+ UniquePtr<DataBuffer> merged(new DataBuffer());
+ merged->Allocate(a->len() + b->len());
+
+ memcpy(merged->data(), a->data(), a->len());
+ memcpy(merged->data() + a->len(), b->data(), b->len());
+
+ return merged;
+ }
+
+ if (a && a->len()) {
+ return a;
+ }
+
+ if (b && b->len()) {
+ return b;
+ }
+
+ return nullptr;
+}
+
+class DummySocket : public NrSocketBase {
+ public:
+ DummySocket()
+ : writable_(UINT_MAX),
+ write_buffer_(nullptr),
+ readable_(UINT_MAX),
+ read_buffer_(nullptr),
+ cb_(nullptr),
+ cb_arg_(nullptr),
+ self_(nullptr) {}
+
+ // the nr_socket APIs
+ virtual int create(nr_transport_addr *addr) {
+ return 0;
+ }
+
+ virtual int sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) {
+ MOZ_CRASH();
+ return 0;
+ }
+
+ virtual int recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) {
+ MOZ_CRASH();
+ return 0;
+ }
+
+ virtual int getaddr(nr_transport_addr *addrp) {
+ MOZ_CRASH();
+ return 0;
+ }
+
+ virtual void close() {
+ }
+
+ virtual int connect(nr_transport_addr *addr) {
+ nr_transport_addr_copy(&connect_addr_, addr);
+ return 0;
+ }
+
+ virtual int listen(int backlog) {
+ return 0;
+ }
+
+ virtual int accept(nr_transport_addr *addrp, nr_socket **sockp) {
+ return 0;
+ }
+
+
+ virtual int write(const void *msg, size_t len, size_t *written) {
+ size_t to_write = std::min(len, writable_);
+
+ if (to_write) {
+ UniquePtr<DataBuffer> msgbuf(new DataBuffer(static_cast<const uint8_t *>(msg), to_write));
+ write_buffer_ = merge(Move(write_buffer_), Move(msgbuf));
+ }
+
+ *written = to_write;
+
+ return 0;
+ }
+
+ virtual int read(void* buf, size_t maxlen, size_t *len) {
+ if (!read_buffer_.get()) {
+ return R_WOULDBLOCK;
+ }
+
+ size_t to_read = std::min(read_buffer_->len(),
+ std::min(maxlen, readable_));
+
+ memcpy(buf, read_buffer_->data(), to_read);
+ *len = to_read;
+
+ if (to_read < read_buffer_->len()) {
+ read_buffer_.reset(new DataBuffer(read_buffer_->data() + to_read,
+ read_buffer_->len() - to_read));
+ } else {
+ read_buffer_.reset();
+ }
+
+ return 0;
+ }
+
+ // Implementations of the async_event APIs.
+ // These are no-ops because we handle scheduling manually
+ // for test purposes.
+ virtual int async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line) {
+ EXPECT_EQ(nullptr, cb_);
+ cb_ = cb;
+ cb_arg_ = cb_arg;
+
+ return 0;
+ }
+
+ virtual int cancel(int how) {
+ cb_ = nullptr;
+ cb_arg_ = nullptr;
+
+ return 0;
+ }
+
+
+ // Read/Manipulate the current state.
+ void CheckWriteBuffer(const uint8_t *data, size_t len) {
+ if (!len) {
+ EXPECT_EQ(nullptr, write_buffer_.get());
+ } else {
+ EXPECT_NE(nullptr, write_buffer_.get());
+ ASSERT_EQ(len, write_buffer_->len());
+ ASSERT_EQ(0, memcmp(data, write_buffer_->data(), len));
+ }
+ }
+
+ void ClearWriteBuffer() {
+ write_buffer_.reset();
+ }
+
+ void SetWritable(size_t val) {
+ writable_ = val;
+ }
+
+ void FireWritableCb() {
+ NR_async_cb cb = cb_;
+ void *cb_arg = cb_arg_;
+
+ cb_ = nullptr;
+ cb_arg_ = nullptr;
+
+ cb(this, NR_ASYNC_WAIT_WRITE, cb_arg);
+ }
+
+ void SetReadBuffer(const uint8_t *data, size_t len) {
+ EXPECT_EQ(nullptr, write_buffer_.get());
+ read_buffer_.reset(new DataBuffer(data, len));
+ }
+
+ void ClearReadBuffer() {
+ read_buffer_.reset();
+ }
+
+ void SetReadable(size_t val) {
+ readable_ = val;
+ }
+
+ nr_socket *get_nr_socket() {
+ if (!self_) {
+ int r = nr_socket_create_int(this, vtbl(), &self_);
+ AddRef();
+ if (r)
+ return nullptr;
+ }
+
+ return self_;
+ }
+
+ nr_transport_addr *get_connect_addr() {
+ return &connect_addr_;
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket);
+
+ private:
+ ~DummySocket() {}
+
+ DISALLOW_COPY_ASSIGN(DummySocket);
+
+ size_t writable_; // Amount we allow someone to write.
+ UniquePtr<DataBuffer> write_buffer_;
+ size_t readable_; // Amount we allow someone to read.
+ UniquePtr<DataBuffer> read_buffer_;
+
+ NR_async_cb cb_;
+ void *cb_arg_;
+ nr_socket *self_;
+
+ nr_transport_addr connect_addr_;
+};
+
+} //namespace mozilla
+
+#endif
+
diff --git a/media/mtransport/test/gtest_ringbuffer_dumper.h b/media/mtransport/test/gtest_ringbuffer_dumper.h
new file mode 100644
index 000000000..81eddbb2f
--- /dev/null
+++ b/media/mtransport/test/gtest_ringbuffer_dumper.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: bcampen@mozilla.com
+
+#ifndef gtest_ringbuffer_dumper_h__
+#define gtest_ringbuffer_dumper_h__
+
+#include "mozilla/SyncRunnable.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
+#include "mtransport_test_utils.h"
+#include "runnable_utils.h"
+#include "rlogconnector.h"
+
+using mozilla::RLogConnector;
+using mozilla::WrapRunnable;
+
+namespace test {
+class RingbufferDumper : public ::testing::EmptyTestEventListener {
+ public:
+ explicit RingbufferDumper(MtransportTestUtils* test_utils) :
+ test_utils_(test_utils)
+ {}
+
+ void ClearRingBuffer_s() {
+ RLogConnector::CreateInstance();
+ // Set limit to zero to clear the ringbuffer
+ RLogConnector::GetInstance()->SetLogLimit(0);
+ RLogConnector::GetInstance()->SetLogLimit(UINT32_MAX);
+ }
+
+ void DestroyRingBuffer_s() {
+ RLogConnector::DestroyInstance();
+ }
+
+ void DumpRingBuffer_s() {
+ std::deque<std::string> logs;
+ // Get an unlimited number of log lines, with no filter
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ for (auto l = logs.begin(); l != logs.end(); ++l) {
+ std::cout << *l << std::endl;
+ }
+ ClearRingBuffer_s();
+ }
+
+ virtual void OnTestStart(const ::testing::TestInfo& testInfo) {
+ mozilla::SyncRunnable::DispatchToThread(
+ test_utils_->sts_target(),
+ WrapRunnable(this, &RingbufferDumper::ClearRingBuffer_s));
+ }
+
+ virtual void OnTestEnd(const ::testing::TestInfo& testInfo) {
+ mozilla::SyncRunnable::DispatchToThread(
+ test_utils_->sts_target(),
+ WrapRunnable(this, &RingbufferDumper::DestroyRingBuffer_s));
+ }
+
+ // Called after a failed assertion or a SUCCEED() invocation.
+ virtual void OnTestPartResult(const ::testing::TestPartResult& testResult) {
+ if (testResult.failed()) {
+ // Dump (and empty) the RLogConnector
+ mozilla::SyncRunnable::DispatchToThread(
+ test_utils_->sts_target(),
+ WrapRunnable(this, &RingbufferDumper::DumpRingBuffer_s));
+ }
+ }
+
+ private:
+ MtransportTestUtils *test_utils_;
+};
+
+} // namespace test
+
+#endif // gtest_ringbuffer_dumper_h__
+
+
+
diff --git a/media/mtransport/test/gtest_utils.h b/media/mtransport/test/gtest_utils.h
new file mode 100644
index 000000000..263a3838c
--- /dev/null
+++ b/media/mtransport/test/gtest_utils.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Utilities to wrap gtest, based on libjingle's gunit
+
+// Some sections of this code are under the following license:
+
+/*
+ * libjingle
+ * Copyright 2004--2008, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Original author: ekr@rtfm.com
+#ifndef gtest_utils__h__
+#define gtest_utils__h__
+
+#include <iostream>
+
+#include "nspr.h"
+#include "prinrval.h"
+#include "prthread.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+
+#include "gtest_ringbuffer_dumper.h"
+#include "mtransport_test_utils.h"
+#include "nss.h"
+#include "ssl.h"
+
+extern "C" {
+#include "registry.h"
+#include "transport_addr.h"
+}
+
+// Wait up to timeout seconds for expression to be true
+#define WAIT(expression, timeout) \
+ do { \
+ for (PRIntervalTime start = PR_IntervalNow(); !(expression) && \
+ ! ((PR_IntervalNow() - start) > PR_MillisecondsToInterval(timeout));) { \
+ PR_Sleep(10); \
+ } \
+ } while(0)
+
+// Same as GTEST_WAIT, but stores the result in res. Used when
+// you also want the result of expression but wish to avoid
+// double evaluation.
+#define WAIT_(expression, timeout, res) \
+ do { \
+ for (PRIntervalTime start = PR_IntervalNow(); !(res = (expression)) && \
+ ! ((PR_IntervalNow() - start) > PR_MillisecondsToInterval(timeout));) { \
+ PR_Sleep(10); \
+ } \
+ } while(0)
+
+#define ASSERT_TRUE_WAIT(expression, timeout) \
+ do { \
+ bool res; \
+ WAIT_(expression, timeout, res); \
+ ASSERT_TRUE(res); \
+ } while(0)
+
+#define EXPECT_TRUE_WAIT(expression, timeout) \
+ do { \
+ bool res; \
+ WAIT_(expression, timeout, res); \
+ EXPECT_TRUE(res); \
+ } while(0)
+
+#define ASSERT_EQ_WAIT(expected, actual, timeout) \
+ do { \
+ WAIT(expected == actual, timeout); \
+ ASSERT_EQ(expected, actual); \
+ } while(0)
+
+using test::RingbufferDumper;
+
+class MtransportTest : public ::testing::Test {
+public:
+ MtransportTest()
+ : test_utils_(nullptr)
+ , dumper_(nullptr)
+ {
+ }
+
+ void SetUp() override {
+ test_utils_ = new MtransportTestUtils();
+ NSS_NoDB_Init(nullptr);
+ NSS_SetDomesticPolicy();
+
+ NR_reg_init(NR_REG_MODE_LOCAL);
+
+ // Attempt to load env vars used by tests.
+ GetEnvironment("TURN_SERVER_ADDRESS", turn_server_);
+ GetEnvironment("TURN_SERVER_USER", turn_user_);
+ GetEnvironment("TURN_SERVER_PASSWORD", turn_password_);
+ GetEnvironment("STUN_SERVER_ADDRESS", stun_server_address_);
+ GetEnvironment("STUN_SERVER_HOSTNAME", stun_server_hostname_);
+
+ std::string disable_non_local;
+ GetEnvironment("MOZ_DISABLE_NONLOCAL_CONNECTIONS", disable_non_local);
+ std::string upload_dir;
+ GetEnvironment("MOZ_UPLOAD_DIR", upload_dir);
+
+ if ((!disable_non_local.empty() && disable_non_local != "0") ||
+ !upload_dir.empty()) {
+ // We're assuming that MOZ_UPLOAD_DIR is only set on tbpl;
+ // MOZ_DISABLE_NONLOCAL_CONNECTIONS probably should be set when running the
+ // cpp unit-tests, but is not presently.
+ stun_server_address_ = "";
+ stun_server_hostname_ = "";
+ turn_server_ = "";
+ }
+
+ // Some tests are flaky and need to check if they're supposed to run.
+ webrtc_enabled_ = CheckEnvironmentFlag("MOZ_WEBRTC_TESTS");
+
+ ::testing::TestEventListeners& listeners =
+ ::testing::UnitTest::GetInstance()->listeners();
+
+ dumper_ = new RingbufferDumper(test_utils_);
+ listeners.Append(dumper_);
+ }
+
+ void TearDown() override {
+ ::testing::UnitTest::GetInstance()->listeners().Release(dumper_);
+ delete dumper_;
+ delete test_utils_;
+ }
+
+ void GetEnvironment(const char* aVar, std::string& out) {
+ char* value = getenv(aVar);
+ if (value) {
+ out = value;
+ }
+ }
+
+ bool CheckEnvironmentFlag(const char* aVar) {
+ std::string value;
+ GetEnvironment(aVar, value);
+ return value == "1";
+ }
+
+ bool WarnIfTurnNotConfigured() const {
+ bool configured =
+ !turn_server_.empty() &&
+ !turn_user_.empty() &&
+ !turn_password_.empty();
+
+ if (configured) {
+ nr_transport_addr addr;
+ if (nr_str_port_to_transport_addr(turn_server_.c_str(), 3478,
+ IPPROTO_UDP, &addr)) {
+ printf("Invalid TURN_SERVER_ADDRESS \"%s\". Only IP numbers supported.\n",
+ turn_server_.c_str());
+ configured = false;
+ }
+ } else {
+ printf(
+ "Set TURN_SERVER_ADDRESS, TURN_SERVER_USER, and TURN_SERVER_PASSWORD\n"
+ "environment variables to run this test\n");
+ }
+
+ return !configured;
+ }
+
+ MtransportTestUtils* test_utils_;
+ RingbufferDumper* dumper_;
+
+ std::string turn_server_;
+ std::string turn_user_;
+ std::string turn_password_;
+ std::string stun_server_address_;
+ std::string stun_server_hostname_;
+
+ bool webrtc_enabled_;
+};
+#endif
diff --git a/media/mtransport/test/ice_unittest.cpp b/media/mtransport/test/ice_unittest.cpp
new file mode 100644
index 000000000..0b3216353
--- /dev/null
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -0,0 +1,3934 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include <algorithm>
+#include <deque>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "sigslot.h"
+
+#include "logging.h"
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+#include "mozilla/Preferences.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+#include "nricectxhandler.h"
+#include "nricemediastream.h"
+#include "nriceresolverfake.h"
+#include "nriceresolver.h"
+#include "nrinterfaceprioritizer.h"
+#include "gtest_ringbuffer_dumper.h"
+#include "rlogconnector.h"
+#include "runnable_utils.h"
+#include "stunserver.h"
+#include "nr_socket_prsock.h"
+#include "test_nr_socket.h"
+#include "ice_ctx.h"
+#include "stun_socket_filter.h"
+#include "mozilla/net/DNS.h"
+
+#include "ice_ctx.h"
+#include "ice_peer_ctx.h"
+#include "ice_media_stream.h"
+
+extern "C" {
+#include "async_timer.h"
+#include "r_data.h"
+#include "util.h"
+#include "r_time.h"
+}
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+
+using namespace mozilla;
+
+static unsigned int kDefaultTimeout = 7000;
+
+//TODO(nils@mozilla.com): This should get replaced with some non-external
+//solution like discussed in bug 860775.
+const std::string kDefaultStunServerHostname(
+ (char *)"global.stun.twilio.com");
+const std::string kBogusStunServerHostname(
+ (char *)"stun-server-nonexistent.invalid");
+const uint16_t kDefaultStunServerPort=3478;
+const std::string kBogusIceCandidate(
+ (char *)"candidate:0 2 UDP 2113601790 192.168.178.20 50769 typ");
+
+const std::string kUnreachableHostIceCandidate(
+ (char *)"candidate:0 1 UDP 2113601790 192.168.178.20 50769 typ host");
+
+namespace {
+
+// DNS resolution helper code
+static std::string
+Resolve(const std::string& fqdn, int address_family)
+{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = address_family;
+ hints.ai_protocol = IPPROTO_UDP;
+ struct addrinfo *res;
+ int err = getaddrinfo(fqdn.c_str(), nullptr, &hints, &res);
+ if (err) {
+ std::cerr << "Error in getaddrinfo: " << err << std::endl;
+ return "";
+ }
+
+ char str_addr[64] = {0};
+ switch (res->ai_family) {
+ case AF_INET:
+ inet_ntop(
+ AF_INET,
+ &reinterpret_cast<struct sockaddr_in*>(res->ai_addr)->sin_addr,
+ str_addr,
+ sizeof(str_addr));
+ break;
+ case AF_INET6:
+ inet_ntop(
+ AF_INET6,
+ &reinterpret_cast<struct sockaddr_in6*>(res->ai_addr)->sin6_addr,
+ str_addr,
+ sizeof(str_addr));
+ break;
+ default:
+ std::cerr << "Got unexpected address family in DNS lookup: "
+ << res->ai_family << std::endl;
+ freeaddrinfo(res);
+ return "";
+ }
+
+ if (!strlen(str_addr)) {
+ std::cerr << "inet_ntop failed" << std::endl;
+ }
+
+ freeaddrinfo(res);
+ return str_addr;
+}
+
+class StunTest : public MtransportTest {
+public:
+ StunTest() : MtransportTest() {
+ stun_server_hostname_ = kDefaultStunServerHostname;
+ }
+
+ void SetUp() override {
+ MtransportTest::SetUp();
+
+ // If only a STUN server FQDN was provided, look up its IP address for the
+ // address-only tests.
+ if (stun_server_address_.empty() && !stun_server_hostname_.empty()) {
+ stun_server_address_ = Resolve(stun_server_hostname_, AF_INET);
+ }
+
+ // Make sure NrIceCtx is in a testable state.
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&NrIceCtx::internal_DeinitializeGlobal),
+ NS_DISPATCH_SYNC);
+
+ // NB: NrIceCtx::internal_DeinitializeGlobal destroys the RLogConnector
+ // singleton.
+ RLogConnector::CreateInstance();
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunServer::GetInstance, AF_INET),
+ NS_DISPATCH_SYNC);
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunServer::GetInstance, AF_INET6),
+ NS_DISPATCH_SYNC);
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET),
+ NS_DISPATCH_SYNC);
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET6),
+ NS_DISPATCH_SYNC);
+ }
+
+ void TearDown() override {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&NrIceCtx::internal_DeinitializeGlobal),
+ NS_DISPATCH_SYNC);
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunServer::ShutdownInstance), NS_DISPATCH_SYNC);
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunTcpServer::ShutdownInstance), NS_DISPATCH_SYNC);
+
+ RLogConnector::DestroyInstance();
+
+ MtransportTest::TearDown();
+ }
+};
+
+enum TrickleMode { TRICKLE_NONE, TRICKLE_SIMULATE, TRICKLE_REAL };
+
+enum ConsentStatus { CONSENT_FRESH, CONSENT_STALE, CONSENT_EXPIRED};
+
+const unsigned int ICE_TEST_PEER_OFFERER = (1 << 0);
+const unsigned int ICE_TEST_PEER_ALLOW_LOOPBACK = (1 << 1);
+const unsigned int ICE_TEST_PEER_ENABLED_TCP = (1 << 2);
+const unsigned int ICE_TEST_PEER_ALLOW_LINK_LOCAL = (1 << 3);
+
+typedef std::string (*CandidateFilter)(const std::string& candidate);
+
+std::vector<std::string> split(const std::string &s, char delim) {
+ std::vector<std::string> elems;
+ std::stringstream ss(s);
+ std::string item;
+ while (std::getline(ss, item, delim)) {
+ elems.push_back(item);
+ }
+ return elems;
+}
+
+static std::string IsSrflxCandidate(const std::string& candidate) {
+ std::vector<std::string> tokens = split(candidate, ' ');
+ if ((tokens.at(6) == "typ") && (tokens.at(7) == "srflx")) {
+ return candidate;
+ }
+ return std::string();
+}
+
+static std::string IsRelayCandidate(const std::string& candidate) {
+ if (candidate.find("typ relay") != std::string::npos) {
+ return candidate;
+ }
+ return std::string();
+}
+
+static std::string IsTcpCandidate(const std::string& candidate) {
+ if (candidate.find("TCP") != std::string::npos) {
+ return candidate;
+ }
+ return std::string();
+}
+
+static std::string IsTcpSoCandidate(const std::string& candidate) {
+ if (candidate.find("tcptype so") != std::string::npos) {
+ return candidate;
+ }
+ return std::string();
+}
+
+static std::string IsLoopbackCandidate(const std::string& candidate) {
+ if (candidate.find("127.0.0.") != std::string::npos) {
+ return candidate;
+ }
+ return std::string();
+}
+
+static std::string IsIpv4Candidate(const std::string& candidate) {
+ std::vector<std::string> tokens = split(candidate, ' ');
+ if (tokens.at(4).find(":") == std::string::npos) {
+ return candidate;
+ }
+ return std::string();
+}
+
+static std::string SabotageHostCandidateAndDropReflexive(
+ const std::string& candidate) {
+ if (candidate.find("typ srflx") != std::string::npos) {
+ return std::string();
+ }
+
+ if (candidate.find("typ host") != std::string::npos) {
+ return kUnreachableHostIceCandidate;
+ }
+
+ return candidate;
+}
+
+bool ContainsSucceededPair(const std::vector<NrIceCandidatePair>& pairs) {
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ if (pairs[i].state == NrIceCandidatePair::STATE_SUCCEEDED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Note: Does not correspond to any notion of prioritization; this is just
+// so we can use stl containers/algorithms that need a comparator
+bool operator<(const NrIceCandidate& lhs,
+ const NrIceCandidate& rhs) {
+ if (lhs.cand_addr.host == rhs.cand_addr.host) {
+ if (lhs.cand_addr.port == rhs.cand_addr.port) {
+ if (lhs.cand_addr.transport == rhs.cand_addr.transport) {
+ if (lhs.type == rhs.type) {
+ return lhs.tcp_type < rhs.tcp_type;
+ }
+ return lhs.type < rhs.type;
+ }
+ return lhs.cand_addr.transport < rhs.cand_addr.transport;
+ }
+ return lhs.cand_addr.port < rhs.cand_addr.port;
+ }
+ return lhs.cand_addr.host < rhs.cand_addr.host;
+}
+
+bool operator==(const NrIceCandidate& lhs,
+ const NrIceCandidate& rhs) {
+ return !((lhs < rhs) || (rhs < lhs));
+}
+
+class IceCandidatePairCompare {
+ public:
+ bool operator()(const NrIceCandidatePair& lhs,
+ const NrIceCandidatePair& rhs) const {
+ if (lhs.priority == rhs.priority) {
+ if (lhs.local == rhs.local) {
+ if (lhs.remote == rhs.remote) {
+ return lhs.codeword < rhs.codeword;
+ }
+ return lhs.remote < rhs.remote;
+ }
+ return lhs.local < rhs.local;
+ }
+ return lhs.priority < rhs.priority;
+ }
+};
+
+class IceTestPeer;
+
+class SchedulableTrickleCandidate {
+ public:
+ SchedulableTrickleCandidate(IceTestPeer *peer,
+ size_t stream,
+ const std::string &candidate,
+ MtransportTestUtils* utils) :
+ peer_(peer),
+ stream_(stream),
+ candidate_(candidate),
+ timer_handle_(nullptr),
+ test_utils_(utils) {
+ }
+
+ ~SchedulableTrickleCandidate() {
+ if (timer_handle_)
+ NR_async_timer_cancel(timer_handle_);
+ }
+
+ void Schedule(unsigned int ms) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &SchedulableTrickleCandidate::Schedule_s, ms),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Schedule_s(unsigned int ms) {
+ MOZ_ASSERT(!timer_handle_);
+ NR_ASYNC_TIMER_SET(ms, Trickle_cb, this, &timer_handle_);
+ }
+
+ static void Trickle_cb(NR_SOCKET s, int how, void *cb_arg) {
+ static_cast<SchedulableTrickleCandidate*>(cb_arg)->Trickle();
+ }
+
+ void Trickle();
+
+ std::string& Candidate() {
+ return candidate_;
+ }
+
+ const std::string& Candidate() const {
+ return candidate_;
+ }
+
+ size_t Stream() const {
+ return stream_;
+ }
+
+ bool IsHost() const {
+ return candidate_.find("typ host") != std::string::npos;
+ }
+
+ bool IsReflexive() const {
+ return candidate_.find("typ srflx") != std::string::npos;
+ }
+
+ bool IsRelay() const {
+ return candidate_.find("typ relay") != std::string::npos;
+ }
+
+ private:
+ IceTestPeer *peer_;
+ size_t stream_;
+ std::string candidate_;
+ void *timer_handle_;
+ MtransportTestUtils* test_utils_;
+
+ DISALLOW_COPY_ASSIGN(SchedulableTrickleCandidate);
+};
+
+class IceTestPeer : public sigslot::has_slots<> {
+ public:
+ // TODO(ekr@rtfm.com): Convert to flags when NrIceCtx::Create() does.
+ // Bug 1193437.
+ IceTestPeer(const std::string& name, MtransportTestUtils* utils,
+ bool offerer,
+ bool allow_loopback = false, bool enable_tcp = true,
+ bool allow_link_local = false,
+ NrIceCtx::Policy ice_policy = NrIceCtx::ICE_POLICY_ALL) :
+ name_(name),
+ ice_ctx_(NrIceCtxHandler::Create(name, offerer, allow_loopback,
+ enable_tcp, allow_link_local,
+ ice_policy)),
+ candidates_(),
+ shutting_down_(false),
+ gathering_complete_(false),
+ ready_ct_(0),
+ ice_connected_(false),
+ ice_failed_(false),
+ ice_reached_checking_(false),
+ received_(0),
+ sent_(0),
+ fake_resolver_(),
+ dns_resolver_(new NrIceResolver()),
+ remote_(nullptr),
+ candidate_filter_(nullptr),
+ expected_local_type_(NrIceCandidate::ICE_HOST),
+ expected_local_transport_(kNrIceTransportUdp),
+ expected_remote_type_(NrIceCandidate::ICE_HOST),
+ trickle_mode_(TRICKLE_NONE),
+ trickled_(0),
+ simulate_ice_lite_(false),
+ nat_(new TestNat),
+ test_utils_(utils) {
+ ice_ctx_->ctx()->SignalGatheringStateChange.connect(
+ this,
+ &IceTestPeer::GatheringStateChange);
+ ice_ctx_->ctx()->SignalConnectionStateChange.connect(
+ this,
+ &IceTestPeer::ConnectionStateChange);
+
+ consent_timestamp_.tv_sec = 0;
+ consent_timestamp_.tv_usec = 0;
+ int r = ice_ctx_->ctx()->SetNat(nat_);
+ (void)r;
+ MOZ_ASSERT(!r);
+ }
+
+ ~IceTestPeer() {
+ test_utils_->sts_target()->Dispatch(WrapRunnable(this,
+ &IceTestPeer::Shutdown),
+ NS_DISPATCH_SYNC);
+
+ // Give the ICE destruction callback time to fire before
+ // we destroy the resolver.
+ PR_Sleep(1000);
+ }
+
+ void AddStream_s(int components) {
+ char name[100];
+ snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(),
+ (int)ice_ctx_->ctx()->GetStreamCount());
+
+ RefPtr<NrIceMediaStream> stream =
+ ice_ctx_->CreateStream(static_cast<char *>(name), components);
+ ice_ctx_->ctx()->SetStream(ice_ctx_->ctx()->GetStreamCount(), stream);
+
+ ASSERT_TRUE(stream);
+ stream->SignalCandidate.connect(this, &IceTestPeer::CandidateInitialized);
+ stream->SignalReady.connect(this, &IceTestPeer::StreamReady);
+ stream->SignalFailed.connect(this, &IceTestPeer::StreamFailed);
+ stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived);
+ }
+
+ void AddStream(int components)
+ {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::AddStream_s, components),
+ NS_DISPATCH_SYNC);
+ }
+
+ void RemoveStream_s(size_t index) {
+ ice_ctx_->ctx()->SetStream(index, nullptr);
+ }
+
+ void RemoveStream(size_t index) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::RemoveStream_s, index),
+ NS_DISPATCH_SYNC);
+ }
+
+ void SetStunServer(const std::string addr, uint16_t port,
+ const char* transport = kNrIceTransportUdp) {
+ if (addr.empty()) {
+ // Happens when MOZ_DISABLE_NONLOCAL_CONNECTIONS is set
+ return;
+ }
+
+ std::vector<NrIceStunServer> stun_servers;
+ UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(
+ addr, port, transport));
+ stun_servers.push_back(*server);
+ SetStunServers(stun_servers);
+ }
+
+ void SetStunServers(const std::vector<NrIceStunServer> &servers) {
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetStunServers(servers)));
+ }
+
+ void UseTestStunServer() {
+ SetStunServer(TestStunServer::GetInstance(AF_INET)->addr(),
+ TestStunServer::GetInstance(AF_INET)->port());
+ }
+
+ void SetTurnServer(const std::string addr, uint16_t port,
+ const std::string username,
+ const std::string password,
+ const char* transport) {
+ std::vector<unsigned char> password_vec(password.begin(), password.end());
+ SetTurnServer(addr, port, username, password_vec, transport);
+ }
+
+
+ void SetTurnServer(const std::string addr, uint16_t port,
+ const std::string username,
+ const std::vector<unsigned char> password,
+ const char* transport) {
+ std::vector<NrIceTurnServer> turn_servers;
+ UniquePtr<NrIceTurnServer> server(NrIceTurnServer::Create(
+ addr, port, username, password, transport));
+ turn_servers.push_back(*server);
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetTurnServers(turn_servers)));
+ }
+
+ void SetTurnServers(const std::vector<NrIceTurnServer> servers) {
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetTurnServers(servers)));
+ }
+
+ void SetFakeResolver(const std::string& ip,
+ const std::string& fqdn) {
+ ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
+ if (!ip.empty() && !fqdn.empty()) {
+ PRNetAddr addr;
+ PRStatus status = PR_StringToNetAddr(ip.c_str(), &addr);
+ addr.inet.port = kDefaultStunServerPort;
+ ASSERT_EQ(PR_SUCCESS, status);
+ fake_resolver_.SetAddr(fqdn, addr);
+ }
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetResolver(
+ fake_resolver_.AllocateResolver())));
+ }
+
+ void SetDNSResolver() {
+ ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetResolver(
+ dns_resolver_->AllocateResolver())));
+ }
+
+ void Gather(bool default_route_only = false) {
+ nsresult res;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res,
+ ice_ctx_->ctx(),
+ &NrIceCtx::StartGathering,
+ default_route_only,
+ false),
+ NS_DISPATCH_SYNC);
+
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ void UseNat() {
+ nat_->enabled_ = true;
+ }
+
+ void SetTimerDivider(int div) {
+ ice_ctx_->ctx()->internal_SetTimerAccelarator(div);
+ }
+
+ void SetStunResponseDelay(uint32_t delay) {
+ nat_->delay_stun_resp_ms_ = delay;
+ }
+
+ void SetFilteringType(TestNat::NatBehavior type) {
+ MOZ_ASSERT(!nat_->has_port_mappings());
+ nat_->filtering_type_ = type;
+ }
+
+ void SetMappingType(TestNat::NatBehavior type) {
+ MOZ_ASSERT(!nat_->has_port_mappings());
+ nat_->mapping_type_ = type;
+ }
+
+ void SetBlockUdp(bool block) {
+ MOZ_ASSERT(!nat_->has_port_mappings());
+ nat_->block_udp_ = block;
+ }
+
+ void SetBlockStun(bool block) {
+ nat_->block_stun_ = block;
+ }
+
+ // Get various pieces of state
+ std::vector<std::string> GetGlobalAttributes() {
+ std::vector<std::string> attrs(ice_ctx_->ctx()->GetGlobalAttributes());
+ if (simulate_ice_lite_) {
+ attrs.push_back("ice-lite");
+ }
+ return attrs;
+ }
+
+ std::vector<std::string> GetCandidates(size_t stream) {
+ std::vector<std::string> v;
+
+ RUN_ON_THREAD(
+ test_utils_->sts_target(),
+ WrapRunnableRet(&v, this, &IceTestPeer::GetCandidates_s, stream));
+
+ return v;
+ }
+
+ std::string FilterCandidate(const std::string& candidate) {
+ if (candidate_filter_) {
+ return candidate_filter_(candidate);
+ }
+ return candidate;
+ }
+
+ std::vector<std::string> GetCandidates_s(size_t stream) {
+ std::vector<std::string> candidates;
+
+ if (stream >= ice_ctx_->ctx()->GetStreamCount() ||
+ !ice_ctx_->ctx()->GetStream(stream)) {
+ EXPECT_TRUE(false) << "No such stream " << stream;
+ return candidates;
+ }
+
+ std::vector<std::string> candidates_in =
+ ice_ctx_->ctx()->GetStream(stream)->GetCandidates();
+
+ for (size_t i=0; i < candidates_in.size(); i++) {
+ std::string candidate(FilterCandidate(candidates_in[i]));
+ if (!candidate.empty()) {
+ std::cerr << name_ << " Returning candidate: "
+ << candidate << std::endl;
+ candidates.push_back(candidate);
+ }
+ }
+
+ return candidates;
+ }
+
+ void SetExpectedTypes(NrIceCandidate::Type local,
+ NrIceCandidate::Type remote,
+ std::string local_transport = kNrIceTransportUdp) {
+ expected_local_type_ = local;
+ expected_local_transport_ = local_transport;
+ expected_remote_type_ = remote;
+ }
+
+ void SetExpectedRemoteCandidateAddr(const std::string& addr) {
+ expected_remote_addr_ = addr;
+ }
+
+ int GetCandidatesPrivateIpv4Range(size_t stream) {
+ std::vector<std::string> candidates = GetCandidates(stream);
+
+ int host_net = 0;
+ for (auto c : candidates) {
+ if (c.find("typ host") != std::string::npos) {
+ nr_transport_addr addr;
+ std::vector<std::string> tokens = split(c, ' ');
+ int r = nr_str_port_to_transport_addr(tokens.at(4).c_str(), 0, IPPROTO_UDP, &addr);
+ MOZ_ASSERT(!r);
+ if (!r && (addr.ip_version == NR_IPV4)) {
+ int n = nr_transport_addr_get_private_addr_range(&addr);
+ if (n) {
+ if (host_net) {
+ // TODO: add support for multiple private interfaces
+ std::cerr << "This test doesn't support multiple private interfaces";
+ return -1;
+ }
+ host_net = n;
+ }
+ }
+ }
+ }
+ return host_net;
+ }
+
+ bool gathering_complete() { return gathering_complete_; }
+ int ready_ct() { return ready_ct_; }
+ bool is_ready_s(size_t stream) {
+ RefPtr<NrIceMediaStream> media_stream = ice_ctx_->ctx()->GetStream(stream);
+ if (!media_stream) {
+ EXPECT_TRUE(false) << "No such stream " << stream;
+ return false;
+ }
+ return media_stream->state() == NrIceMediaStream::ICE_OPEN;
+ }
+ bool is_ready(size_t stream)
+ {
+ bool result;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&result, this, &IceTestPeer::is_ready_s, stream),
+ NS_DISPATCH_SYNC);
+ return result;
+ }
+ bool ice_connected() { return ice_connected_; }
+ bool ice_failed() { return ice_failed_; }
+ bool ice_reached_checking() { return ice_reached_checking_; }
+ size_t received() { return received_; }
+ size_t sent() { return sent_; }
+
+
+ void RestartIce() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::RestartIce_s,
+ ice_ctx_->CreateCtx()),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void RestartIce_s(RefPtr<NrIceCtx> new_ctx) {
+ ice_ctx_->BeginIceRestart(new_ctx);
+
+ // set signals for the newly restarted ctx
+ ice_ctx_->ctx()->SignalGatheringStateChange.connect(
+ this,
+ &IceTestPeer::GatheringStateChange);
+ ice_ctx_->ctx()->SignalConnectionStateChange.connect(
+ this,
+ &IceTestPeer::ConnectionStateChange);
+
+ // take care of some local bookkeeping
+ ready_ct_ = 0;
+ gathering_complete_ = false;
+ ice_connected_ = false;
+ ice_failed_ = false;
+ ice_reached_checking_ = false;
+ remote_ = nullptr;
+ }
+
+
+ void FinalizeIceRestart() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::FinalizeIceRestart_s),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void FinalizeIceRestart_s() {
+ ice_ctx_->FinalizeIceRestart();
+ }
+
+
+ void RollbackIceRestart() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::RollbackIceRestart_s),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void RollbackIceRestart_s() {
+ ice_ctx_->RollbackIceRestart();
+ }
+
+
+ // Start connecting to another peer
+ void Connect_s(IceTestPeer *remote, TrickleMode trickle_mode,
+ bool start = true) {
+ nsresult res;
+
+ remote_ = remote;
+
+ trickle_mode_ = trickle_mode;
+ ice_connected_ = false;
+ ice_failed_ = false;
+ ice_reached_checking_ = false;
+ res = ice_ctx_->ctx()->ParseGlobalAttributes(remote->GetGlobalAttributes());
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+
+ if (trickle_mode == TRICKLE_NONE ||
+ trickle_mode == TRICKLE_REAL) {
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ RefPtr<NrIceMediaStream> aStream = ice_ctx_->ctx()->GetStream(i);
+ if (!aStream || aStream->HasParsedAttributes()) {
+ continue;
+ }
+ std::vector<std::string> candidates =
+ remote->GetCandidates(i);
+
+ for (size_t j=0; j<candidates.size(); ++j) {
+ std::cerr << name_ << " Adding remote candidate: " + candidates[j] << std::endl;
+ }
+ res = aStream->ParseAttributes(candidates);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+ } else {
+ // Parse empty attributes and then trickle them out later
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ RefPtr<NrIceMediaStream> aStream = ice_ctx_->ctx()->GetStream(i);
+ if (!aStream || aStream->HasParsedAttributes()) {
+ continue;
+ }
+ std::vector<std::string> empty_attrs;
+ std::cout << "Calling ParseAttributes on stream " << i << std::endl;
+ res = aStream->ParseAttributes(empty_attrs);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+ }
+
+ if (start) {
+ // Now start checks
+ res = ice_ctx_->ctx()->StartChecks();
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+ }
+
+ void Connect(IceTestPeer *remote, TrickleMode trickle_mode,
+ bool start = true) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &IceTestPeer::Connect_s, remote, trickle_mode, start),
+ NS_DISPATCH_SYNC);
+ }
+
+ void SimulateTrickle(size_t stream) {
+ std::cerr << name_ << " Doing trickle for stream " << stream << std::endl;
+ // If we are in trickle deferred mode, now trickle in the candidates
+ // for |stream|
+
+ // We should be safe here since stream changes happen on STS thread.
+ ASSERT_GT(remote_->ice_ctx_->ctx()->GetStreamCount(), stream);
+ ASSERT_TRUE(remote_->ice_ctx_->ctx()->GetStream(stream).get());
+
+ std::vector<SchedulableTrickleCandidate*>& candidates =
+ ControlTrickle(stream);
+
+ for (auto i = candidates.begin(); i != candidates.end(); ++i) {
+ (*i)->Schedule(0);
+ }
+ }
+
+ // Allows test case to completely control when/if candidates are trickled
+ // (test could also do things like insert extra trickle candidates, or
+ // change existing ones, or insert duplicates, really anything is fair game)
+ std::vector<SchedulableTrickleCandidate*>& ControlTrickle(size_t stream) {
+ std::cerr << "Doing controlled trickle for stream " << stream << std::endl;
+
+ std::vector<std::string> candidates =
+ remote_->GetCandidates(stream);
+
+ for (size_t j=0; j<candidates.size(); j++) {
+ controlled_trickle_candidates_[stream].push_back(
+ new SchedulableTrickleCandidate(
+ this, stream, candidates[j], test_utils_));
+ }
+
+ return controlled_trickle_candidates_[stream];
+ }
+
+ nsresult TrickleCandidate_s(const std::string &candidate, size_t stream) {
+ if (!ice_ctx_->ctx()->GetStream(stream)) {
+ // stream might have gone away before the trickle timer popped
+ return NS_OK;
+ }
+ return ice_ctx_->ctx()->GetStream(stream)->ParseTrickleCandidate(candidate);
+ }
+
+ void DumpCandidate(std::string which, const NrIceCandidate& cand) {
+ std::string type;
+ std::string tcp_type;
+
+ std::string addr;
+ int port;
+
+ if (which.find("Remote") != std::string::npos) {
+ addr = cand.cand_addr.host;
+ port = cand.cand_addr.port;
+ }
+ else {
+ addr = cand.local_addr.host;
+ port = cand.local_addr.port;
+ }
+ switch(cand.type) {
+ case NrIceCandidate::ICE_HOST:
+ type = "host";
+ break;
+ case NrIceCandidate::ICE_SERVER_REFLEXIVE:
+ type = "srflx";
+ break;
+ case NrIceCandidate::ICE_PEER_REFLEXIVE:
+ type = "prflx";
+ break;
+ case NrIceCandidate::ICE_RELAYED:
+ type = "relay";
+ if (which.find("Local") != std::string::npos) {
+ type += "(" + cand.local_addr.transport + ")";
+ }
+ break;
+ default:
+ FAIL();
+ };
+
+ switch(cand.tcp_type) {
+ case NrIceCandidate::ICE_NONE:
+ break;
+ case NrIceCandidate::ICE_ACTIVE:
+ tcp_type = " tcptype=active";
+ break;
+ case NrIceCandidate::ICE_PASSIVE:
+ tcp_type = " tcptype=passive";
+ break;
+ case NrIceCandidate::ICE_SO:
+ tcp_type = " tcptype=so";
+ break;
+ default:
+ FAIL();
+ };
+
+
+ std::cerr << which
+ << " --> "
+ << type
+ << " "
+ << addr
+ << ":"
+ << port
+ << "/"
+ << cand.cand_addr.transport
+ << tcp_type
+ << " codeword="
+ << cand.codeword
+ << std::endl;
+ }
+
+ void DumpAndCheckActiveCandidates_s() {
+ std::cerr << name_ << " Active candidates:" << std::endl;
+ for (size_t i=0; i < ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ if (!ice_ctx_->ctx()->GetStream(i)) {
+ continue;
+ }
+
+ for (size_t j=0; j < ice_ctx_->ctx()->GetStream(i)->components(); ++j) {
+ std::cerr << name_ << " Stream " << i
+ << " component " << j+1 << std::endl;
+
+ UniquePtr<NrIceCandidate> local;
+ UniquePtr<NrIceCandidate> remote;
+
+ nsresult res = ice_ctx_->ctx()->GetStream(i)->GetActivePair(j+1,
+ &local,
+ &remote);
+ if (res == NS_ERROR_NOT_AVAILABLE) {
+ std::cerr << "Component unpaired or disabled." << std::endl;
+ } else {
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ DumpCandidate("Local ", *local);
+ /* Depending on timing, and the whims of the network
+ * stack/configuration we're running on top of, prflx is always a
+ * possibility. */
+ if (expected_local_type_ == NrIceCandidate::ICE_HOST) {
+ ASSERT_NE(NrIceCandidate::ICE_SERVER_REFLEXIVE, local->type);
+ ASSERT_NE(NrIceCandidate::ICE_RELAYED, local->type);
+ } else {
+ ASSERT_EQ(expected_local_type_, local->type);
+ }
+ ASSERT_EQ(expected_local_transport_, local->local_addr.transport);
+ DumpCandidate("Remote ", *remote);
+ /* Depending on timing, and the whims of the network
+ * stack/configuration we're running on top of, prflx is always a
+ * possibility. */
+ if (expected_remote_type_ == NrIceCandidate::ICE_HOST) {
+ ASSERT_NE(NrIceCandidate::ICE_SERVER_REFLEXIVE, remote->type);
+ ASSERT_NE(NrIceCandidate::ICE_RELAYED, remote->type);
+ } else {
+ ASSERT_EQ(expected_remote_type_, remote->type);
+ }
+ if (!expected_remote_addr_.empty()) {
+ ASSERT_EQ(expected_remote_addr_, remote->cand_addr.host);
+ }
+ }
+ }
+ }
+ }
+
+ void DumpAndCheckActiveCandidates() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::DumpAndCheckActiveCandidates_s),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Close() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(ice_ctx_->ctx(), &NrIceCtx::destroy_peer_ctx),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Shutdown() {
+ std::cerr << name_ << " Shutdown" << std::endl;
+ shutting_down_ = true;
+ for (auto s = controlled_trickle_candidates_.begin();
+ s != controlled_trickle_candidates_.end();
+ ++s) {
+ for (auto cand = s->second.begin(); cand != s->second.end(); ++cand) {
+ delete *cand;
+ }
+ }
+
+ ice_ctx_ = nullptr;
+
+ if (remote_) {
+ remote_->UnsetRemote();
+ remote_ = nullptr;
+ }
+ }
+
+ void UnsetRemote()
+ {
+ remote_ = nullptr;
+ }
+
+ void StartChecks() {
+ nsresult res;
+
+ // Now start checks
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, ice_ctx_->ctx(), &NrIceCtx::StartChecks),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ // Handle events
+ void GatheringStateChange(NrIceCtx* ctx,
+ NrIceCtx::GatheringState state) {
+ if (shutting_down_) {
+ return;
+ }
+ if (state != NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
+ return;
+ }
+
+ std::cerr << name_ << " Gathering complete" << std::endl;
+ gathering_complete_ = true;
+
+ std::cerr << name_ << " CANDIDATES:" << std::endl;
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ std::cerr << "Stream " << name_ << std::endl;
+
+ if (!ice_ctx_->ctx()->GetStream(i)) {
+ std::cerr << "DISABLED" << std::endl;
+ continue;
+ }
+
+ std::vector<std::string> candidates =
+ ice_ctx_->ctx()->GetStream(i)->GetCandidates();
+
+ for(size_t j=0; j<candidates.size(); ++j) {
+ std::cerr << candidates[j] << std::endl;
+ }
+ }
+ std::cerr << std::endl;
+
+ }
+
+ void CandidateInitialized(NrIceMediaStream *stream, const std::string &raw_candidate) {
+ std::string candidate(FilterCandidate(raw_candidate));
+ if (candidate.empty()) {
+ return;
+ }
+ std::cerr << "Candidate for stream " << stream->name() << " initialized: "
+ << candidate << std::endl;
+ candidates_[stream->name()].push_back(candidate);
+
+ // If we are connected, then try to trickle to the other side.
+ if (remote_ && remote_->remote_ && (trickle_mode_ != TRICKLE_SIMULATE)) {
+ // first, find the index of the stream we've been given so
+ // we can get the corresponding stream on the remote side
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ if (ice_ctx_->ctx()->GetStream(i) == stream) {
+ RefPtr<NrIceCtx> ctx = remote_->ice_ctx_->ctx();
+ ASSERT_GT(ctx->GetStreamCount(), i);
+ nsresult res = ctx->GetStream(i)->ParseTrickleCandidate(candidate);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ ++trickled_;
+ return;
+ }
+ }
+ ADD_FAILURE() << "No matching stream found for " << stream;
+ }
+ }
+
+ nsresult GetCandidatePairs_s(size_t stream_index,
+ std::vector<NrIceCandidatePair>* pairs)
+ {
+ MOZ_ASSERT(pairs);
+ if (stream_index >= ice_ctx_->ctx()->GetStreamCount() ||
+ !ice_ctx_->ctx()->GetStream(stream_index)) {
+ // Is there a better error for "no such index"?
+ ADD_FAILURE() << "No such media stream index: " << stream_index;
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return ice_ctx_->ctx()->GetStream(stream_index)->GetCandidatePairs(pairs);
+ }
+
+ nsresult GetCandidatePairs(size_t stream_index,
+ std::vector<NrIceCandidatePair>* pairs) {
+ nsresult v;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&v, this,
+ &IceTestPeer::GetCandidatePairs_s,
+ stream_index,
+ pairs),
+ NS_DISPATCH_SYNC);
+ return v;
+ }
+
+ void DumpCandidatePair(const NrIceCandidatePair& pair) {
+ std::cerr << std::endl;
+ DumpCandidate("Local", pair.local);
+ DumpCandidate("Remote", pair.remote);
+ std::cerr << "state = " << pair.state
+ << " priority = " << pair.priority
+ << " nominated = " << pair.nominated
+ << " selected = " << pair.selected
+ << " codeword = " << pair.codeword << std::endl;
+ }
+
+ void DumpCandidatePairs_s(NrIceMediaStream *stream) {
+ std::vector<NrIceCandidatePair> pairs;
+ nsresult res = stream->GetCandidatePairs(&pairs);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+
+ std::cerr << "Begin list of candidate pairs [" << std::endl;
+
+ for (std::vector<NrIceCandidatePair>::iterator p = pairs.begin();
+ p != pairs.end(); ++p) {
+ DumpCandidatePair(*p);
+ }
+ std::cerr << "]" << std::endl;
+ }
+
+ void DumpCandidatePairs_s() {
+ std::cerr << "Dumping candidate pairs for all streams [" << std::endl;
+ for (size_t s = 0; s < ice_ctx_->ctx()->GetStreamCount(); ++s) {
+ if (!ice_ctx_->ctx()->GetStream(s)) {
+ continue;
+ }
+ DumpCandidatePairs_s(ice_ctx_->ctx()->GetStream(s).get());
+ }
+ std::cerr << "]" << std::endl;
+ }
+
+ bool CandidatePairsPriorityDescending(const std::vector<NrIceCandidatePair>&
+ pairs) {
+ // Verify that priority is descending
+ uint64_t priority = std::numeric_limits<uint64_t>::max();
+
+ for (size_t p = 0; p < pairs.size(); ++p) {
+ if (priority < pairs[p].priority) {
+ std::cerr << "Priority increased in subsequent pairs:" << std::endl;
+ DumpCandidatePair(pairs[p-1]);
+ DumpCandidatePair(pairs[p]);
+ return false;
+ } else if (priority == pairs[p].priority) {
+ if (!IceCandidatePairCompare()(pairs[p], pairs[p-1]) &&
+ !IceCandidatePairCompare()(pairs[p-1], pairs[p])) {
+ std::cerr << "Ignoring identical pair from trigger check" << std::endl;
+ } else {
+ std::cerr << "Duplicate priority in subseqent pairs:" << std::endl;
+ DumpCandidatePair(pairs[p-1]);
+ DumpCandidatePair(pairs[p]);
+ return false;
+ }
+ }
+ priority = pairs[p].priority;
+ }
+ return true;
+ }
+
+ void UpdateAndValidateCandidatePairs(size_t stream_index,
+ std::vector<NrIceCandidatePair>*
+ new_pairs) {
+ std::vector<NrIceCandidatePair> old_pairs = *new_pairs;
+ GetCandidatePairs(stream_index, new_pairs);
+ ASSERT_TRUE(CandidatePairsPriorityDescending(*new_pairs)) << "New list of "
+ "candidate pairs is either not sorted in priority order, or has "
+ "duplicate priorities.";
+ ASSERT_TRUE(CandidatePairsPriorityDescending(old_pairs)) << "Old list of "
+ "candidate pairs is either not sorted in priority order, or has "
+ "duplicate priorities. This indicates some bug in the test case.";
+ std::vector<NrIceCandidatePair> added_pairs;
+ std::vector<NrIceCandidatePair> removed_pairs;
+
+ // set_difference computes the set of elements that are present in the
+ // first set, but not the second
+ // NrIceCandidatePair::operator< compares based on the priority, local
+ // candidate, and remote candidate in that order. This means this will
+ // catch cases where the priority has remained the same, but one of the
+ // candidates has changed.
+ std::set_difference((*new_pairs).begin(),
+ (*new_pairs).end(),
+ old_pairs.begin(),
+ old_pairs.end(),
+ std::inserter(added_pairs, added_pairs.begin()),
+ IceCandidatePairCompare());
+
+ std::set_difference(old_pairs.begin(),
+ old_pairs.end(),
+ (*new_pairs).begin(),
+ (*new_pairs).end(),
+ std::inserter(removed_pairs, removed_pairs.begin()),
+ IceCandidatePairCompare());
+
+ for (std::vector<NrIceCandidatePair>::iterator a = added_pairs.begin();
+ a != added_pairs.end(); ++a) {
+ std::cerr << "Found new candidate pair." << std::endl;
+ DumpCandidatePair(*a);
+ }
+
+ for (std::vector<NrIceCandidatePair>::iterator r = removed_pairs.begin();
+ r != removed_pairs.end(); ++r) {
+ std::cerr << "Pre-existing candidate pair is now missing:" << std::endl;
+ DumpCandidatePair(*r);
+ }
+
+ ASSERT_TRUE(removed_pairs.empty()) << "At least one candidate pair has "
+ "gone missing.";
+ }
+
+ void StreamReady(NrIceMediaStream *stream) {
+ ++ready_ct_;
+ std::cerr << name_ << " Stream ready for " << stream->name()
+ << " ct=" << ready_ct_ << std::endl;
+ DumpCandidatePairs_s(stream);
+ }
+ void StreamFailed(NrIceMediaStream *stream) {
+ std::cerr << name_ << " Stream failed for " << stream->name()
+ << " ct=" << ready_ct_ << std::endl;
+ DumpCandidatePairs_s(stream);
+ }
+
+ void ConnectionStateChange(NrIceCtx* ctx,
+ NrIceCtx::ConnectionState state) {
+ (void)ctx;
+ switch (state) {
+ case NrIceCtx::ICE_CTX_INIT:
+ break;
+ case NrIceCtx::ICE_CTX_CHECKING:
+ std::cerr << name_ << " ICE reached checking" << std::endl;
+ ice_reached_checking_ = true;
+ break;
+ case NrIceCtx::ICE_CTX_CONNECTED:
+ std::cerr << name_ << " ICE connected" << std::endl;
+ ice_connected_ = true;
+ break;
+ case NrIceCtx::ICE_CTX_COMPLETED:
+ std::cerr << name_ << " ICE completed" << std::endl;
+ break;
+ case NrIceCtx::ICE_CTX_FAILED:
+ std::cerr << name_ << " ICE failed" << std::endl;
+ ice_failed_ = true;
+ break;
+ case NrIceCtx::ICE_CTX_DISCONNECTED:
+ std::cerr << name_ << " ICE disconnected" << std::endl;
+ ice_connected_ = false;
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ }
+
+ void PacketReceived(NrIceMediaStream *stream, int component, const unsigned char *data,
+ int len) {
+ std::cerr << name_ << ": received " << len << " bytes" << std::endl;
+ ++received_;
+ }
+
+ void SendPacket(int stream, int component, const unsigned char *data,
+ int len) {
+ RefPtr<NrIceMediaStream> media_stream = ice_ctx_->ctx()->GetStream(stream);
+ if (!media_stream) {
+ ADD_FAILURE() << "No such stream " << stream;
+ return;
+ }
+
+ ASSERT_TRUE(NS_SUCCEEDED(media_stream->SendPacket(component, data, len)));
+
+ ++sent_;
+ std::cerr << name_ << ": sent " << len << " bytes" << std::endl;
+ }
+
+ void SendFailure(int stream, int component) {
+ RefPtr<NrIceMediaStream> media_stream = ice_ctx_->ctx()->GetStream(stream);
+ if (!media_stream) {
+ ADD_FAILURE() << "No such stream " << stream;
+ return;
+ }
+
+ const std::string d("FAIL");
+ ASSERT_TRUE(NS_FAILED(media_stream->SendPacket(component,
+ reinterpret_cast<const unsigned char *>(d.c_str()), d.length())));
+
+ std::cerr << name_ << ": send failed as expected" << std::endl;
+ }
+
+ void SetCandidateFilter(CandidateFilter filter) {
+ candidate_filter_ = filter;
+ }
+
+ void ParseCandidate_s(size_t i, const std::string& candidate) {
+ ASSERT_TRUE(ice_ctx_->ctx()->GetStream(i).get()) << "No such stream " << i;
+
+ std::vector<std::string> attributes;
+
+ attributes.push_back(candidate);
+ ice_ctx_->ctx()->GetStream(i)->ParseAttributes(attributes);
+ }
+
+ void ParseCandidate(size_t i, const std::string& candidate)
+ {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::ParseCandidate_s,
+ i,
+ candidate),
+ NS_DISPATCH_SYNC);
+ }
+
+ void DisableComponent_s(size_t stream, int component_id) {
+ ASSERT_LT(stream, ice_ctx_->ctx()->GetStreamCount());
+ ASSERT_TRUE(ice_ctx_->ctx()->GetStream(stream).get()) << "No such stream "
+ << stream;
+ nsresult res =
+ ice_ctx_->ctx()->GetStream(stream)->DisableComponent(component_id);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ void DisableComponent(size_t stream, int component_id)
+ {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::DisableComponent_s,
+ stream,
+ component_id),
+ NS_DISPATCH_SYNC);
+ }
+
+ void AssertConsentRefresh_s(size_t stream, int component_id, ConsentStatus status) {
+ ASSERT_LT(stream, ice_ctx_->ctx()->GetStreamCount());
+ ASSERT_TRUE(ice_ctx_->ctx()->GetStream(stream).get()) << "No such stream "
+ << stream;
+ bool can_send;
+ struct timeval timestamp;
+ nsresult res = ice_ctx_->ctx()->GetStream(stream)->
+ GetConsentStatus(component_id, &can_send, &timestamp);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ if (status == CONSENT_EXPIRED) {
+ ASSERT_EQ(can_send, 0);
+ } else {
+ ASSERT_EQ(can_send, 1);
+ }
+ if (consent_timestamp_.tv_sec) {
+ if (status == CONSENT_FRESH) {
+ ASSERT_EQ(r_timeval_cmp(&timestamp, &consent_timestamp_), 1);
+ } else {
+ ASSERT_EQ(r_timeval_cmp(&timestamp, &consent_timestamp_), 0);
+ }
+ }
+ consent_timestamp_.tv_sec = timestamp.tv_sec;
+ consent_timestamp_.tv_usec = timestamp.tv_usec;
+ std::cerr << name_ << ": new consent timestamp = " <<
+ consent_timestamp_.tv_sec << "." << consent_timestamp_.tv_usec <<
+ std::endl;
+ }
+
+ void AssertConsentRefresh(ConsentStatus status) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::AssertConsentRefresh_s,
+ 0,
+ 1,
+ status),
+ NS_DISPATCH_SYNC);
+ }
+
+ int trickled() { return trickled_; }
+
+ void SetControlling(NrIceCtx::Controlling controlling) {
+ nsresult res;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, ice_ctx_->ctx(),
+ &NrIceCtx::SetControlling,
+ controlling),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ NrIceCtx::Controlling GetControlling() {
+ return ice_ctx_->ctx()->GetControlling();
+ }
+
+ void SetTiebreaker(uint64_t tiebreaker) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::SetTiebreaker_s,
+ tiebreaker),
+ NS_DISPATCH_SYNC);
+ }
+
+ void SetTiebreaker_s(uint64_t tiebreaker) {
+ ice_ctx_->ctx()->peer()->tiebreaker = tiebreaker;
+ }
+
+ void SimulateIceLite() {
+ simulate_ice_lite_ = true;
+ SetControlling(NrIceCtx::ICE_CONTROLLED);
+ }
+
+ nsresult GetDefaultCandidate(unsigned int stream, NrIceCandidate* cand) {
+ nsresult rv;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&rv, this,
+ &IceTestPeer::GetDefaultCandidate_s,
+ stream, cand),
+ NS_DISPATCH_SYNC);
+
+ return rv;
+ }
+
+ nsresult GetDefaultCandidate_s(unsigned int stream, NrIceCandidate* cand) {
+ return ice_ctx_->ctx()->GetStream(stream)->GetDefaultCandidate(1, cand);
+ }
+
+ private:
+ std::string name_;
+ RefPtr<NrIceCtxHandler> ice_ctx_;
+ std::map<std::string, std::vector<std::string> > candidates_;
+ // Maps from stream id to list of remote trickle candidates
+ std::map<size_t, std::vector<SchedulableTrickleCandidate*> >
+ controlled_trickle_candidates_;
+ bool shutting_down_;
+ bool gathering_complete_;
+ int ready_ct_;
+ bool ice_connected_;
+ bool ice_failed_;
+ bool ice_reached_checking_;
+ size_t received_;
+ size_t sent_;
+ struct timeval consent_timestamp_;
+ NrIceResolverFake fake_resolver_;
+ RefPtr<NrIceResolver> dns_resolver_;
+ IceTestPeer *remote_;
+ CandidateFilter candidate_filter_;
+ NrIceCandidate::Type expected_local_type_;
+ std::string expected_local_transport_;
+ NrIceCandidate::Type expected_remote_type_;
+ std::string expected_remote_addr_;
+ TrickleMode trickle_mode_;
+ int trickled_;
+ bool simulate_ice_lite_;
+ RefPtr<mozilla::TestNat> nat_;
+ MtransportTestUtils* test_utils_;
+};
+
+void SchedulableTrickleCandidate::Trickle() {
+ timer_handle_ = nullptr;
+ nsresult res = peer_->TrickleCandidate_s(candidate_, stream_);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+}
+
+class WebRtcIceGatherTest : public StunTest {
+ public:
+ void SetUp() override {
+ StunTest::SetUp();
+
+ Preferences::SetInt("media.peerconnection.ice.tcp_so_sock_count", 3);
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(TestStunServer::GetInstance(AF_INET),
+ &TestStunServer::Reset),
+ NS_DISPATCH_SYNC);
+ if (TestStunServer::GetInstance(AF_INET6)) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(TestStunServer::GetInstance(AF_INET6),
+ &TestStunServer::Reset),
+ NS_DISPATCH_SYNC);
+ }
+ }
+
+ void TearDown() override {
+ peer_ = nullptr;
+ StunTest::TearDown();
+ }
+
+ void EnsurePeer(const unsigned int flags = ICE_TEST_PEER_OFFERER) {
+ if (!peer_) {
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_,
+ flags & ICE_TEST_PEER_OFFERER,
+ flags & ICE_TEST_PEER_ALLOW_LOOPBACK,
+ flags & ICE_TEST_PEER_ENABLED_TCP,
+ flags & ICE_TEST_PEER_ALLOW_LINK_LOCAL);
+ peer_->AddStream(1);
+ }
+ }
+
+ void Gather(unsigned int waitTime = kDefaultTimeout) {
+ EnsurePeer();
+ peer_->Gather();
+
+ if (waitTime) {
+ WaitForGather(waitTime);
+ }
+ }
+
+ void WaitForGather(unsigned int waitTime = kDefaultTimeout) {
+ ASSERT_TRUE_WAIT(peer_->gathering_complete(), waitTime);
+ }
+
+ void AddStunServerWithResponse(
+ const std::string& fake_addr,
+ uint16_t fake_port,
+ const std::string& fqdn,
+ const std::string& proto,
+ std::vector<NrIceStunServer>* stun_servers) {
+ int family;
+ if (fake_addr.find(':') != std::string::npos) {
+ family = AF_INET6;
+ } else {
+ family = AF_INET;
+ }
+
+ std::string stun_addr;
+ uint16_t stun_port;
+ if (proto == kNrIceTransportUdp) {
+ TestStunServer::GetInstance(family)->SetResponseAddr(fake_addr,
+ fake_port);
+ stun_addr = TestStunServer::GetInstance(family)->addr();
+ stun_port = TestStunServer::GetInstance(family)->port();
+ } else if (proto == kNrIceTransportTcp) {
+ TestStunTcpServer::GetInstance(family)->SetResponseAddr(fake_addr,
+ fake_port);
+ stun_addr = TestStunTcpServer::GetInstance(family)->addr();
+ stun_port = TestStunTcpServer::GetInstance(family)->port();
+ } else {
+ MOZ_CRASH();
+ }
+
+ if (!fqdn.empty()) {
+ peer_->SetFakeResolver(stun_addr, fqdn);
+ stun_addr = fqdn;
+ }
+
+ stun_servers->push_back(*NrIceStunServer::Create(stun_addr,
+ stun_port,
+ proto.c_str()));
+ }
+
+ void UseFakeStunUdpServerWithResponse(
+ const std::string& fake_addr,
+ uint16_t fake_port,
+ const std::string& fqdn = std::string()) {
+ EnsurePeer();
+ std::vector<NrIceStunServer> stun_servers;
+ AddStunServerWithResponse(fake_addr, fake_port, fqdn, "udp", &stun_servers);
+ peer_->SetStunServers(stun_servers);
+ }
+
+ void UseFakeStunTcpServerWithResponse(
+ const std::string& fake_addr,
+ uint16_t fake_port,
+ const std::string& fqdn = std::string()) {
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ std::vector<NrIceStunServer> stun_servers;
+ AddStunServerWithResponse(fake_addr, fake_port, fqdn, "tcp", &stun_servers);
+ peer_->SetStunServers(stun_servers);
+ }
+
+ void UseFakeStunUdpTcpServersWithResponse(
+ const std::string& fake_udp_addr,
+ uint16_t fake_udp_port,
+ const std::string& fake_tcp_addr,
+ uint16_t fake_tcp_port) {
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ std::vector<NrIceStunServer> stun_servers;
+ AddStunServerWithResponse(fake_udp_addr,
+ fake_udp_port,
+ "", // no fqdn
+ "udp",
+ &stun_servers);
+ AddStunServerWithResponse(fake_tcp_addr,
+ fake_tcp_port,
+ "", // no fqdn
+ "tcp",
+ &stun_servers);
+
+ peer_->SetStunServers(stun_servers);
+ }
+
+ void UseTestStunServer() {
+ TestStunServer::GetInstance(AF_INET)->Reset();
+ peer_->SetStunServer(TestStunServer::GetInstance(AF_INET)->addr(),
+ TestStunServer::GetInstance(AF_INET)->port());
+ }
+
+ // NB: Only does substring matching, watch out for stuff like "1.2.3.4"
+ // matching "21.2.3.47". " 1.2.3.4 " should not have false positives.
+ bool StreamHasMatchingCandidate(unsigned int stream,
+ const std::string& match,
+ const std::string& match2 = "") {
+ std::vector<std::string> candidates = peer_->GetCandidates(stream);
+ for (size_t c = 0; c < candidates.size(); ++c) {
+ if (std::string::npos != candidates[c].find(match)) {
+ if (!match2.length() ||
+ std::string::npos != candidates[c].find(match2)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void DumpCandidates(unsigned int stream) {
+ std::vector<std::string> candidates = peer_->GetCandidates(stream);
+
+ std::cerr << "Candidates for stream " << stream << "->"
+ << candidates.size() << std::endl;
+
+ for (auto c : candidates) {
+ std::cerr << "Candidate: " << c << std::endl;
+ }
+ }
+
+ protected:
+ mozilla::UniquePtr<IceTestPeer> peer_;
+};
+
+class WebRtcIceConnectTest : public StunTest {
+ public:
+ WebRtcIceConnectTest() :
+ initted_(false),
+ test_stun_server_inited_(false),
+ use_nat_(false),
+ filtering_type_(TestNat::ENDPOINT_INDEPENDENT),
+ mapping_type_(TestNat::ENDPOINT_INDEPENDENT),
+ block_udp_(false) {}
+
+ void SetUp() override {
+ StunTest::SetUp();
+
+ nsresult rv;
+ target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ }
+
+ void TearDown() override {
+ p1_ = nullptr;
+ p2_ = nullptr;
+
+ StunTest::TearDown();
+ }
+
+ void AddStream(int components) {
+ Init(false, false);
+ p1_->AddStream(components);
+ p2_->AddStream(components);
+ }
+
+ void RemoveStream(size_t index) {
+ p1_->RemoveStream(index);
+ p2_->RemoveStream(index);
+ }
+
+ void Init(bool allow_loopback,
+ bool enable_tcp,
+ bool setup_stun_servers = true,
+ NrIceCtx::Policy ice_policy = NrIceCtx::ICE_POLICY_ALL) {
+ if (initted_) {
+ return;
+ }
+
+ p1_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, allow_loopback,
+ enable_tcp, false, ice_policy);
+ p2_ = MakeUnique<IceTestPeer>("P2", test_utils_, false, allow_loopback,
+ enable_tcp, false, ice_policy);
+ InitPeer(p1_.get(), setup_stun_servers);
+ InitPeer(p2_.get(), setup_stun_servers);
+
+ initted_ = true;
+ }
+
+ void InitPeer(IceTestPeer* peer, bool setup_stun_servers = true) {
+ if (use_nat_) {
+ // If we enable nat simulation, but still use a real STUN server somewhere
+ // on the internet, we will see failures if there is a real NAT in
+ // addition to our simulated one, particularly if it disallows
+ // hairpinning.
+ if (setup_stun_servers) {
+ InitTestStunServer();
+ peer->UseTestStunServer();
+ }
+ peer->UseNat();
+ peer->SetFilteringType(filtering_type_);
+ peer->SetMappingType(mapping_type_);
+ peer->SetBlockUdp(block_udp_);
+ } else if (setup_stun_servers) {
+ std::vector<NrIceStunServer> stun_servers;
+
+ stun_servers.push_back(*NrIceStunServer::Create(stun_server_address_,
+ kDefaultStunServerPort, kNrIceTransportUdp));
+ stun_servers.push_back(*NrIceStunServer::Create(stun_server_address_,
+ kDefaultStunServerPort, kNrIceTransportTcp));
+
+ peer->SetStunServers(stun_servers);
+ }
+ }
+
+ bool Gather(unsigned int waitTime = kDefaultTimeout,
+ bool default_route_only = false) {
+ Init(false, false);
+
+ return GatherCallerAndCallee(p1_.get(),
+ p2_.get(),
+ waitTime,
+ default_route_only);
+ }
+
+ bool GatherCallerAndCallee(IceTestPeer* caller,
+ IceTestPeer* callee,
+ unsigned int waitTime = kDefaultTimeout,
+ bool default_route_only = false) {
+ caller->Gather(default_route_only);
+ callee->Gather(default_route_only);
+
+ if (waitTime) {
+ EXPECT_TRUE_WAIT(caller->gathering_complete(), waitTime);
+ if (!caller->gathering_complete())
+ return false;
+ EXPECT_TRUE_WAIT(callee->gathering_complete(), waitTime);
+ if (!callee->gathering_complete())
+ return false;
+ }
+ return true;
+ }
+
+ void UseNat() {
+ // to be useful, this method should be called before Init
+ ASSERT_FALSE(initted_);
+ use_nat_ = true;
+ }
+
+ void SetFilteringType(TestNat::NatBehavior type) {
+ // to be useful, this method should be called before Init
+ ASSERT_FALSE(initted_);
+ filtering_type_ = type;
+ }
+
+ void SetMappingType(TestNat::NatBehavior type) {
+ // to be useful, this method should be called before Init
+ ASSERT_FALSE(initted_);
+ mapping_type_ = type;
+ }
+
+ void BlockUdp() {
+ // note: |block_udp_| is used only in InitPeer.
+ // Use IceTestPeer::SetBlockUdp to act on the peer directly.
+ block_udp_ = true;
+ }
+
+ void SetupAndCheckConsent() {
+ p1_->SetTimerDivider(10);
+ p2_->SetTimerDivider(10);
+ ASSERT_TRUE(Gather());
+ Connect();
+ p1_->AssertConsentRefresh(CONSENT_FRESH);
+ p2_->AssertConsentRefresh(CONSENT_FRESH);
+ SendReceive();
+ }
+
+ void AssertConsentRefresh(ConsentStatus status = CONSENT_FRESH) {
+ p1_->AssertConsentRefresh(status);
+ p2_->AssertConsentRefresh(status);
+ }
+
+ void InitTestStunServer() {
+ if (test_stun_server_inited_) {
+ return;
+ }
+
+ std::cerr << "Resetting TestStunServer" << std::endl;
+ TestStunServer::GetInstance(AF_INET)->Reset();
+ test_stun_server_inited_ = true;
+ }
+
+ void UseTestStunServer() {
+ InitTestStunServer();
+ p1_->UseTestStunServer();
+ p2_->UseTestStunServer();
+ }
+
+ void SetTurnServer(const std::string addr, uint16_t port,
+ const std::string username,
+ const std::string password,
+ const char* transport = kNrIceTransportUdp) {
+ p1_->SetTurnServer(addr, port, username, password, transport);
+ p2_->SetTurnServer(addr, port, username, password, transport);
+ }
+
+ void SetTurnServers(const std::vector<NrIceTurnServer>& servers) {
+ p1_->SetTurnServers(servers);
+ p2_->SetTurnServers(servers);
+ }
+
+ void SetCandidateFilter(CandidateFilter filter, bool both=true) {
+ p1_->SetCandidateFilter(filter);
+ if (both) {
+ p2_->SetCandidateFilter(filter);
+ }
+ }
+
+ void Connect() {
+ ConnectCallerAndCallee(p1_.get(), p2_.get());
+ }
+
+ void ConnectCallerAndCallee(IceTestPeer* caller, IceTestPeer* callee) {
+ ASSERT_TRUE(caller->ready_ct() == 0);
+ ASSERT_TRUE(caller->ice_connected() == 0);
+ ASSERT_TRUE(caller->ice_reached_checking() == 0);
+ ASSERT_TRUE(callee->ready_ct() == 0);
+ ASSERT_TRUE(callee->ice_connected() == 0);
+ ASSERT_TRUE(callee->ice_reached_checking() == 0);
+
+ // IceTestPeer::Connect grabs attributes from the first arg, and
+ // gives them to |this|, meaning that callee->Connect(caller, ...)
+ // simulates caller sending an offer to callee. Order matters here
+ // because it determines which peer is controlling.
+ callee->Connect(caller, TRICKLE_NONE);
+ caller->Connect(callee, TRICKLE_NONE);
+
+ ASSERT_TRUE_WAIT(caller->ready_ct() == 1 && callee->ready_ct() == 1,
+ kDefaultTimeout);
+ ASSERT_TRUE_WAIT(caller->ice_connected() && callee->ice_connected(),
+ kDefaultTimeout);
+
+ ASSERT_TRUE(caller->ice_reached_checking());
+ ASSERT_TRUE(callee->ice_reached_checking());
+
+ caller->DumpAndCheckActiveCandidates();
+ callee->DumpAndCheckActiveCandidates();
+ }
+
+ void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote,
+ std::string transport = kNrIceTransportUdp) {
+ p1_->SetExpectedTypes(local, remote, transport);
+ p2_->SetExpectedTypes(local, remote, transport);
+ }
+
+ void SetExpectedTypes(NrIceCandidate::Type local1, NrIceCandidate::Type remote1,
+ NrIceCandidate::Type local2, NrIceCandidate::Type remote2) {
+ p1_->SetExpectedTypes(local1, remote1);
+ p2_->SetExpectedTypes(local2, remote2);
+ }
+
+ void SetExpectedRemoteCandidateAddr(const std::string& addr) {
+ p1_->SetExpectedRemoteCandidateAddr(addr);
+ p2_->SetExpectedRemoteCandidateAddr(addr);
+ }
+
+ void ConnectP1(TrickleMode mode = TRICKLE_NONE) {
+ p1_->Connect(p2_.get(), mode);
+ }
+
+ void ConnectP2(TrickleMode mode = TRICKLE_NONE) {
+ p2_->Connect(p1_.get(), mode);
+ }
+
+ void WaitForConnectedStreams(int expected_streams = 1) {
+ ASSERT_TRUE_WAIT(p1_->ready_ct() == expected_streams &&
+ p2_->ready_ct() == expected_streams, kDefaultTimeout);
+ ASSERT_TRUE_WAIT(p1_->ice_connected() && p2_->ice_connected(),
+ kDefaultTimeout);
+ }
+
+ void AssertCheckingReached() {
+ ASSERT_TRUE(p1_->ice_reached_checking());
+ ASSERT_TRUE(p2_->ice_reached_checking());
+ }
+
+ void WaitForConnected(unsigned int timeout = kDefaultTimeout) {
+ ASSERT_TRUE_WAIT(p1_->ice_connected(), timeout);
+ ASSERT_TRUE_WAIT(p2_->ice_connected(), timeout);
+ }
+
+ void WaitForGather() {
+ ASSERT_TRUE_WAIT(p1_->gathering_complete(), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(p2_->gathering_complete(), kDefaultTimeout);
+ }
+
+ void WaitForDisconnected(unsigned int timeout = kDefaultTimeout) {
+ ASSERT_TRUE(p1_->ice_connected());
+ ASSERT_TRUE(p2_->ice_connected());
+ ASSERT_TRUE_WAIT(p1_->ice_connected() == 0 &&
+ p2_->ice_connected() == 0,
+ timeout);
+ }
+
+ void WaitForFailed(unsigned int timeout = kDefaultTimeout) {
+ ASSERT_TRUE_WAIT(p1_->ice_failed() &&
+ p2_->ice_failed(),
+ timeout);
+ }
+
+ void ConnectTrickle(TrickleMode trickle = TRICKLE_SIMULATE) {
+ p2_->Connect(p1_.get(), trickle);
+ p1_->Connect(p2_.get(), trickle);
+ }
+
+ void SimulateTrickle(size_t stream) {
+ p1_->SimulateTrickle(stream);
+ p2_->SimulateTrickle(stream);
+ ASSERT_TRUE_WAIT(p1_->is_ready(stream), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(p2_->is_ready(stream), kDefaultTimeout);
+ }
+
+ void SimulateTrickleP1(size_t stream) {
+ p1_->SimulateTrickle(stream);
+ }
+
+ void SimulateTrickleP2(size_t stream) {
+ p2_->SimulateTrickle(stream);
+ }
+
+ void CloseP1() {
+ p1_->Close();
+ }
+
+ void ConnectThenDelete() {
+ p2_->Connect(p1_.get(), TRICKLE_NONE, false);
+ p1_->Connect(p2_.get(), TRICKLE_NONE, true);
+ test_utils_->sts_target()->Dispatch(WrapRunnable(this,
+ &WebRtcIceConnectTest::CloseP1),
+ NS_DISPATCH_SYNC);
+ p2_->StartChecks();
+
+ // Wait to see if we crash
+ PR_Sleep(PR_MillisecondsToInterval(kDefaultTimeout));
+ }
+
+ // default is p1_ sending to p2_
+ void SendReceive() {
+ SendReceive(p1_.get(), p2_.get());
+ }
+
+ void SendReceive(IceTestPeer *p1, IceTestPeer *p2,
+ bool expect_tx_failure = false,
+ bool expect_rx_failure = false) {
+ size_t previousSent = p1->sent();
+ size_t previousReceived = p2->received();
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(p1,
+ &IceTestPeer::SendPacket, 0, 1,
+ reinterpret_cast<const unsigned char *>("TEST"), 4),
+ NS_DISPATCH_SYNC);
+
+ if (expect_tx_failure) {
+ ASSERT_EQ(previousSent, p1->sent());
+ } else {
+ ASSERT_EQ(previousSent+1, p1->sent());
+ }
+ if (expect_rx_failure) {
+ usleep(1000);
+ ASSERT_EQ(previousReceived, p2->received());
+ } else {
+ ASSERT_TRUE_WAIT(p2->received() == previousReceived+1, 1000);
+ }
+ }
+
+ void SendFailure() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(p1_.get(),
+ &IceTestPeer::SendFailure, 0, 1),
+ NS_DISPATCH_SYNC);
+ }
+
+ protected:
+ bool initted_;
+ bool test_stun_server_inited_;
+ nsCOMPtr<nsIEventTarget> target_;
+ mozilla::UniquePtr<IceTestPeer> p1_;
+ mozilla::UniquePtr<IceTestPeer> p2_;
+ bool use_nat_;
+ TestNat::NatBehavior filtering_type_;
+ TestNat::NatBehavior mapping_type_;
+ bool block_udp_;
+};
+
+class WebRtcIcePrioritizerTest : public StunTest {
+ public:
+ WebRtcIcePrioritizerTest():
+ prioritizer_(nullptr) {}
+
+ ~WebRtcIcePrioritizerTest() {
+ if (prioritizer_) {
+ nr_interface_prioritizer_destroy(&prioritizer_);
+ }
+ }
+
+ void SetPriorizer(nr_interface_prioritizer *prioritizer) {
+ prioritizer_ = prioritizer;
+ }
+
+ void AddInterface(const std::string& num, int type, int estimated_speed) {
+ std::string str_addr = "10.0.0." + num;
+ std::string ifname = "eth" + num;
+ nr_local_addr local_addr;
+ local_addr.interface.type = type;
+ local_addr.interface.estimated_speed = estimated_speed;
+
+ int r = nr_str_port_to_transport_addr(str_addr.c_str(), 0,
+ IPPROTO_UDP, &(local_addr.addr));
+ ASSERT_EQ(0, r);
+ strncpy(local_addr.addr.ifname, ifname.c_str(), MAXIFNAME);
+
+ r = nr_interface_prioritizer_add_interface(prioritizer_, &local_addr);
+ ASSERT_EQ(0, r);
+ r = nr_interface_prioritizer_sort_preference(prioritizer_);
+ ASSERT_EQ(0, r);
+ }
+
+ void HasLowerPreference(const std::string& num1, const std::string& num2) {
+ std::string key1 = "eth" + num1 + ":10.0.0." + num1;
+ std::string key2 = "eth" + num2 + ":10.0.0." + num2;
+ UCHAR pref1, pref2;
+ int r = nr_interface_prioritizer_get_priority(prioritizer_, key1.c_str(), &pref1);
+ ASSERT_EQ(0, r);
+ r = nr_interface_prioritizer_get_priority(prioritizer_, key2.c_str(), &pref2);
+ ASSERT_EQ(0, r);
+ ASSERT_LE(pref1, pref2);
+ }
+
+ private:
+ nr_interface_prioritizer *prioritizer_;
+};
+
+class WebRtcIcePacketFilterTest : public StunTest {
+ public:
+ WebRtcIcePacketFilterTest(): udp_filter_(nullptr),
+ tcp_filter_(nullptr) {}
+
+ void SetUp() {
+ StunTest::SetUp();
+
+ // Set up enough of the ICE ctx to allow the packet filter to work
+ ice_ctx_ = NrIceCtxHandler::Create("test", true);
+
+ nsCOMPtr<nsISocketFilterHandler> udp_handler =
+ do_GetService(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID);
+ ASSERT_TRUE(udp_handler);
+ udp_handler->NewFilter(getter_AddRefs(udp_filter_));
+
+ nsCOMPtr<nsISocketFilterHandler> tcp_handler =
+ do_GetService(NS_STUN_TCP_SOCKET_FILTER_HANDLER_CONTRACTID);
+ ASSERT_TRUE(tcp_handler);
+ tcp_handler->NewFilter(getter_AddRefs(tcp_filter_));
+ }
+
+ void TearDown() {
+ test_utils_->sts_target()->Dispatch(WrapRunnable(this,
+ &WebRtcIcePacketFilterTest::TearDown_s),
+ NS_DISPATCH_SYNC);
+ StunTest::TearDown();
+ }
+
+ void TearDown_s() {
+ ice_ctx_ = nullptr;
+ }
+
+ void TestIncoming(const uint8_t* data, uint32_t len,
+ uint8_t from_addr, int from_port,
+ bool expected_result) {
+ mozilla::net::NetAddr addr;
+ MakeNetAddr(&addr, from_addr, from_port);
+ bool result;
+ nsresult rv = udp_filter_->FilterPacket(&addr, data, len,
+ nsISocketFilter::SF_INCOMING,
+ &result);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(expected_result, result);
+ }
+
+ void TestIncomingTcp(const uint8_t* data, uint32_t len,
+ bool expected_result) {
+ mozilla::net::NetAddr addr;
+ bool result;
+ nsresult rv = tcp_filter_->FilterPacket(&addr, data, len,
+ nsISocketFilter::SF_INCOMING,
+ &result);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(expected_result, result);
+ }
+
+ void TestIncomingTcpFramed(const uint8_t* data, uint32_t len,
+ bool expected_result) {
+ mozilla::net::NetAddr addr;
+ bool result;
+ uint8_t* framed_data = new uint8_t[len+2];
+ framed_data[0] = htons(len);
+ memcpy(&framed_data[2], data, len);
+ nsresult rv = tcp_filter_->FilterPacket(&addr, framed_data, len+2,
+ nsISocketFilter::SF_INCOMING,
+ &result);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(expected_result, result);
+ delete[] framed_data;
+ }
+
+ void TestOutgoing(const uint8_t* data, uint32_t len,
+ uint8_t to_addr, int to_port,
+ bool expected_result) {
+ mozilla::net::NetAddr addr;
+ MakeNetAddr(&addr, to_addr, to_port);
+ bool result;
+ nsresult rv = udp_filter_->FilterPacket(&addr, data, len,
+ nsISocketFilter::SF_OUTGOING,
+ &result);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(expected_result, result);
+ }
+
+ void TestOutgoingTcp(const uint8_t* data, uint32_t len,
+ bool expected_result) {
+ mozilla::net::NetAddr addr;
+ bool result;
+ nsresult rv = tcp_filter_->FilterPacket(&addr, data, len,
+ nsISocketFilter::SF_OUTGOING,
+ &result);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(expected_result, result);
+ }
+
+ void TestOutgoingTcpFramed(const uint8_t* data, uint32_t len,
+ bool expected_result) {
+ mozilla::net::NetAddr addr;
+ bool result;
+ uint8_t* framed_data = new uint8_t[len+2];
+ framed_data[0] = htons(len);
+ memcpy(&framed_data[2], data, len);
+ nsresult rv = tcp_filter_->FilterPacket(&addr, framed_data, len+2,
+ nsISocketFilter::SF_OUTGOING,
+ &result);
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_EQ(expected_result, result);
+ delete[] framed_data;
+ }
+
+ private:
+ void MakeNetAddr(mozilla::net::NetAddr* net_addr,
+ uint8_t last_digit, uint16_t port) {
+ net_addr->inet.family = AF_INET;
+ net_addr->inet.ip = 192 << 24 | 168 << 16 | 1 << 8 | last_digit;
+ net_addr->inet.port = port;
+ }
+
+ nsCOMPtr<nsISocketFilter> udp_filter_;
+ nsCOMPtr<nsISocketFilter> tcp_filter_;
+ RefPtr<NrIceCtxHandler> ice_ctx_;
+};
+} // end namespace
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerHostnameNoResolver) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerTcpHostnameNoResolver) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerIpAddress) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort);
+ peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherStunServerIpAddressNoHost) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, false, false, false, NrIceCtx::ICE_POLICY_NO_HOST);
+ peer_->AddStream(1);
+ peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort);
+ peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_);
+ Gather();
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " host "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerHostname) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunBogusHostname) {
+ EnsurePeer();
+ peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort);
+ peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerIpAddress) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "typ srflx raddr"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerIpAddressTcp) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype passive"));
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype passive", " 9 "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype so"));
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype so", " 9 "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype active", " 9 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerHostname) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "typ srflx raddr"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerHostnameTcp) {
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype passive"));
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype passive", " 9 "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype so"));
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "tcptype so", " 9 "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "tcptype active", " 9 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerHostnameBothUdpTcp) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ std::vector<NrIceStunServer> stun_servers;
+
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ stun_servers.push_back(*NrIceStunServer::Create(stun_server_hostname_,
+ kDefaultStunServerPort, kNrIceTransportUdp));
+ stun_servers.push_back(*NrIceStunServer::Create(stun_server_hostname_,
+ kDefaultStunServerPort, kNrIceTransportTcp));
+ peer_->SetStunServers(stun_servers);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerIpAddressBothUdpTcp) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ std::vector<NrIceStunServer> stun_servers;
+
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ stun_servers.push_back(*NrIceStunServer::Create(stun_server_address_,
+ kDefaultStunServerPort, kNrIceTransportUdp));
+ stun_servers.push_back(*NrIceStunServer::Create(stun_server_address_,
+ kDefaultStunServerPort, kNrIceTransportTcp));
+ peer_->SetStunServers(stun_servers);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunBogusHostname) {
+ EnsurePeer();
+ peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunBogusHostnameTcp) {
+ EnsurePeer(ICE_TEST_PEER_OFFERER | ICE_TEST_PEER_ENABLED_TCP);
+ peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ peer_->SetDNSResolver();
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestDefaultCandidate) {
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ Gather();
+ NrIceCandidate default_candidate;
+ ASSERT_TRUE(NS_SUCCEEDED(peer_->GetDefaultCandidate(0, &default_candidate)));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherTurn) {
+ EnsurePeer();
+ if (turn_server_.empty())
+ return;
+ peer_->SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_, kNrIceTransportUdp);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherTurnTcp) {
+ EnsurePeer();
+ if (turn_server_.empty())
+ return;
+ peer_->SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_, kNrIceTransportTcp);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDisableComponent) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->AddStream(2);
+ peer_->DisableComponent(1, 2);
+ Gather();
+ std::vector<std::string> candidates =
+ peer_->GetCandidates(1);
+
+ for (size_t i=0; i<candidates.size(); ++i) {
+ size_t sp1 = candidates[i].find(' ');
+ ASSERT_EQ(0, candidates[i].compare(sp1+1, 1, "1", 1));
+ }
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherVerifyNoLoopback) {
+ Gather();
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "127.0.0.1"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherAllowLoopback) {
+ // Set up peer with loopback allowed.
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, true);
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "127.0.0.1"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherTcpDisabled) {
+ // Set up peer with tcp disabled.
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, false, false);
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " TCP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+}
+
+// Verify that a bogus candidate doesn't cause crashes on the
+// main thread. See bug 856433.
+TEST_F(WebRtcIceGatherTest, TestBogusCandidate) {
+ Gather();
+ peer_->ParseCandidate(0, kBogusIceCandidate);
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunServer) {
+ UseFakeStunUdpServerWithResponse("192.0.2.133", 3333);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.133 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunTcpServer) {
+ UseFakeStunTcpServerWithResponse("192.0.2.233", 3333);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.233 3333 typ srflx",
+ " tcptype "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunServerV6) {
+ if (!TestStunServer::GetInstance(AF_INET6)) {
+ // No V6 addresses
+ return;
+ }
+ UseFakeStunUdpServerWithResponse("beef::", 3333);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " beef:: 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunServerFQDN) {
+ UseFakeStunUdpServerWithResponse("192.0.2.133", 3333, "stun.example.com");
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.133 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunServerV6FQDN) {
+ if (!TestStunServer::GetInstance(AF_INET6)) {
+ // No V6 addresses
+ return;
+ }
+ UseFakeStunUdpServerWithResponse("beef::", 3333, "stun.example.com");
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " beef:: 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsWildcardAddr) {
+ UseFakeStunUdpServerWithResponse("0.0.0.0", 3333);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 0.0.0.0 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsWildcardAddrV6) {
+ if (!TestStunServer::GetInstance(AF_INET6)) {
+ // No V6 addresses
+ return;
+ }
+ UseFakeStunUdpServerWithResponse("::", 3333);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " :: "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsPort0) {
+ UseFakeStunUdpServerWithResponse("192.0.2.133", 0);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.2.133 0 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsLoopbackAddr) {
+ UseFakeStunUdpServerWithResponse("127.0.0.133", 3333);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 127.0.0.133 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsLoopbackAddrV6) {
+ if (!TestStunServer::GetInstance(AF_INET6)) {
+ // No V6 addresses
+ return;
+ }
+ UseFakeStunUdpServerWithResponse("::1", 3333);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " ::1 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerTrickle) {
+ UseFakeStunUdpServerWithResponse("192.0.2.1", 3333);
+ TestStunServer::GetInstance(AF_INET)->SetDropInitialPackets(3);
+ Gather(0);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "192.0.2.1"));
+ WaitForGather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "192.0.2.1"));
+}
+
+// Test no host with our fake STUN server and apparently NATted.
+TEST_F(WebRtcIceGatherTest, TestFakeStunServerNatedNoHost) {
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, false, false, false, NrIceCtx::ICE_POLICY_NO_HOST);
+ peer_->AddStream(1);
+ UseFakeStunUdpServerWithResponse("192.0.2.1", 3333);
+ Gather(0);
+ WaitForGather();
+ DumpCandidates(0);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "host"));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "srflx"));
+ NrIceCandidate default_candidate;
+ nsresult rv = peer_->GetDefaultCandidate(0, &default_candidate);
+ if (NS_SUCCEEDED(rv)) {
+ ASSERT_NE(NrIceCandidate::ICE_HOST, default_candidate.type);
+ }
+}
+
+// Test no host with our fake STUN server and apparently non-NATted.
+TEST_F(WebRtcIceGatherTest, TestFakeStunServerNoNatNoHost) {
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, false, false, false, NrIceCtx::ICE_POLICY_NO_HOST);
+ peer_->AddStream(1);
+ UseTestStunServer();
+ Gather(0);
+ WaitForGather();
+ DumpCandidates(0);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "host"));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "srflx"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunTcpServerTrickle) {
+ UseFakeStunTcpServerWithResponse("192.0.3.1", 3333);
+ TestStunTcpServer::GetInstance(AF_INET)->SetDelay(500);
+ Gather(0);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype "));
+ WaitForGather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunTcpAndUdpServerTrickle) {
+ UseFakeStunUdpTcpServersWithResponse("192.0.2.1", 3333, "192.0.3.1", 3333);
+ TestStunServer::GetInstance(AF_INET)->SetDropInitialPackets(3);
+ TestStunTcpServer::GetInstance(AF_INET)->SetDelay(500);
+ Gather(0);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "192.0.2.1", "UDP"));
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype "));
+ WaitForGather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "192.0.2.1", "UDP"));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.3.1 ", " tcptype "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestSetIceControlling) {
+ EnsurePeer();
+ peer_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ NrIceCtx::Controlling controlling = peer_->GetControlling();
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, controlling);
+ // SetControlling should only allow setting this once
+ peer_->SetControlling(NrIceCtx::ICE_CONTROLLED);
+ controlling = peer_->GetControlling();
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, controlling);
+}
+
+TEST_F(WebRtcIceGatherTest, TestSetIceControlled) {
+ EnsurePeer();
+ peer_->SetControlling(NrIceCtx::ICE_CONTROLLED);
+ NrIceCtx::Controlling controlling = peer_->GetControlling();
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, controlling);
+ // SetControlling should only allow setting this once
+ peer_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ controlling = peer_->GetControlling();
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, controlling);
+}
+
+TEST_F(WebRtcIceConnectTest, TestGather) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherTcp) {
+ Init(false, true);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherAutoPrioritize) {
+ Init(false, false);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+
+TEST_F(WebRtcIceConnectTest, TestConnect) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+
+TEST_F(WebRtcIceConnectTest, TestConnectRestartIce) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+ SendReceive(p1_.get(), p2_.get());
+
+ p2_->RestartIce();
+ ASSERT_FALSE(p2_->gathering_complete());
+
+ // verify p1 and p2 streams are still connected after restarting ice on p2
+ SendReceive(p1_.get(), p2_.get());
+
+ mozilla::UniquePtr<IceTestPeer> p3_;
+ p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, false, false, false);
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+
+ p2_->AddStream(1);
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ ConnectCallerAndCallee(p3_.get(), p2_.get());
+ SendReceive(p1_.get(), p2_.get()); // p1 and p2 still connected
+ SendReceive(p3_.get(), p2_.get()); // p3 and p2 are now connected
+
+ p2_->FinalizeIceRestart();
+ SendReceive(p3_.get(), p2_.get()); // p3 and p2 are still connected
+
+ SendReceive(p1_.get(), p2_.get(), false, true); // p1 and p2 not connected
+
+ p3_ = nullptr;
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectRestartIceThenAbort) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+ SendReceive(p1_.get(), p2_.get());
+
+ p2_->RestartIce();
+ ASSERT_FALSE(p2_->gathering_complete());
+
+ // verify p1 and p2 streams are still connected after restarting ice on p2
+ SendReceive(p1_.get(), p2_.get());
+
+ mozilla::UniquePtr<IceTestPeer> p3_;
+ p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, false, false, false);
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+
+ p2_->AddStream(1);
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ ConnectCallerAndCallee(p3_.get(), p2_.get());
+ SendReceive(p1_.get(), p2_.get()); // p1 and p2 still connected
+ SendReceive(p3_.get(), p2_.get()); // p3 and p2 are now connected
+
+ p2_->RollbackIceRestart();
+ SendReceive(p1_.get(), p2_.get()); // p1 and p2 are still connected
+
+ SendReceive(p3_.get(), p2_.get(), false, true); // p3 and p2 not connected
+
+ p3_ = nullptr;
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectSetControllingAfterIceRestart) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ // Just for fun lets do this with switched rolls
+ p1_->SetControlling(NrIceCtx::ICE_CONTROLLED);
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ Connect();
+ SendReceive(p1_.get(), p2_.get());
+ // Set rolls should not switch by connecting
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p1_->GetControlling());
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p2_->GetControlling());
+
+ p2_->RestartIce();
+ ASSERT_FALSE(p2_->gathering_complete());
+ // ICE restart should allow us to set control role again
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLED);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p2_->GetControlling());
+ // But still only allowed to set control role once
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p2_->GetControlling());
+
+ mozilla::UniquePtr<IceTestPeer> p3_;
+ p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, false, false, false);
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+ // Set control role for p3 accordingly (w/o role conflict)
+ p3_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p3_->GetControlling());
+
+ p2_->AddStream(1);
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ ConnectCallerAndCallee(p3_.get(), p2_.get());
+ // Again connecting should not result in role switch
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p2_->GetControlling());
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p3_->GetControlling());
+
+ p2_->FinalizeIceRestart();
+ // And again we are not allowed to switch roles at this point any more
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p2_->GetControlling());
+ p3_->SetControlling(NrIceCtx::ICE_CONTROLLED);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p3_->GetControlling());
+
+ p3_ = nullptr;
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTcp) {
+ Init(false, true);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsTcpCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
+ Connect();
+}
+
+//TCP SO tests works on localhost only with delay applied:
+// tc qdisc add dev lo root netem delay 10ms
+TEST_F(WebRtcIceConnectTest, DISABLED_TestConnectTcpSo) {
+ Init(false, true);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsTcpSoCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
+ Connect();
+}
+
+// Disabled because this breaks with hairpinning.
+TEST_F(WebRtcIceConnectTest, DISABLED_TestConnectNoHost) {
+ Init(false, false, false, NrIceCtx::ICE_POLICY_NO_HOST);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE, kNrIceTransportTcp);
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestLoopbackOnlySortOf) {
+ Init(true, false, false);
+ AddStream(1);
+ SetCandidateFilter(IsLoopbackCandidate);
+ ASSERT_TRUE(Gather());
+ SetExpectedRemoteCandidateAddr("127.0.0.1");
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectBothControllingP1Wins) {
+ AddStream(1);
+ p1_->SetTiebreaker(1);
+ p2_->SetTiebreaker(0);
+ ASSERT_TRUE(Gather());
+ p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectBothControllingP2Wins) {
+ AddStream(1);
+ p1_->SetTiebreaker(0);
+ p2_->SetTiebreaker(1);
+ ASSERT_TRUE(Gather());
+ p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectIceLiteOfferer) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ p1_->SimulateIceLite();
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestTrickleBothControllingP1Wins) {
+ AddStream(1);
+ p1_->SetTiebreaker(1);
+ p2_->SetTiebreaker(0);
+ ASSERT_TRUE(Gather());
+ p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ ConnectTrickle();
+ SimulateTrickle(0);
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestTrickleBothControllingP2Wins) {
+ AddStream(1);
+ p1_->SetTiebreaker(0);
+ p2_->SetTiebreaker(1);
+ ASSERT_TRUE(Gather());
+ p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ ConnectTrickle();
+ SimulateTrickle(0);
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestTrickleIceLiteOfferer) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ p1_->SimulateIceLite();
+ ConnectTrickle();
+ SimulateTrickle(0);
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherFullCone) {
+ UseNat();
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherFullConeAutoPrioritize) {
+ UseNat();
+ Init(true, false);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+
+TEST_F(WebRtcIceConnectTest, TestConnectFullCone) {
+ UseNat();
+ AddStream(1);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectNoNatNoHost) {
+ Init(false, false, false, NrIceCtx::ICE_POLICY_NO_HOST);
+ AddStream(1);
+ UseTestStunServer();
+ // Because we are connecting from our host candidate to the
+ // other side's apparent srflx (which is also their host)
+ // we see a host/srflx pair.
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectFullConeNoHost) {
+ UseNat();
+ Init(false, false, false, NrIceCtx::ICE_POLICY_NO_HOST);
+ AddStream(1);
+ UseTestStunServer();
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherAddressRestrictedCone) {
+ UseNat();
+ SetFilteringType(TestNat::ADDRESS_DEPENDENT);
+ SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectAddressRestrictedCone) {
+ UseNat();
+ SetFilteringType(TestNat::ADDRESS_DEPENDENT);
+ SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
+ AddStream(1);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherPortRestrictedCone) {
+ UseNat();
+ SetFilteringType(TestNat::PORT_DEPENDENT);
+ SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectPortRestrictedCone) {
+ UseNat();
+ SetFilteringType(TestNat::PORT_DEPENDENT);
+ SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
+ AddStream(1);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherSymmetricNat) {
+ UseNat();
+ SetFilteringType(TestNat::PORT_DEPENDENT);
+ SetMappingType(TestNat::PORT_DEPENDENT);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectSymmetricNat) {
+ if (turn_server_.empty())
+ return;
+
+ UseNat();
+ SetFilteringType(TestNat::PORT_DEPENDENT);
+ SetMappingType(TestNat::PORT_DEPENDENT);
+ AddStream(1);
+ p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED);
+ p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectSymmetricNatAndNoNat) {
+ p1_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, false, false);
+ p1_->UseNat();
+ p1_->SetFilteringType(TestNat::PORT_DEPENDENT);
+ p1_->SetMappingType(TestNat::PORT_DEPENDENT);
+
+ p2_ = MakeUnique<IceTestPeer>("P2", test_utils_, false, false, false);
+ initted_ = true;
+
+ AddStream(1);
+ p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_PEER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_HOST);
+ p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_PEER_REFLEXIVE);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherNatBlocksUDP) {
+ if (turn_server_.empty())
+ return;
+
+ UseNat();
+ BlockUdp();
+ AddStream(1);
+ std::vector<NrIceTurnServer> turn_servers;
+ std::vector<unsigned char> password_vec(turn_password_.begin(),
+ turn_password_.end());
+ turn_servers.push_back(
+ *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort,
+ turn_user_, password_vec, kNrIceTransportTcp));
+ turn_servers.push_back(
+ *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort,
+ turn_user_, password_vec, kNrIceTransportUdp));
+ SetTurnServers(turn_servers);
+ // We have to wait for the UDP-based stuff to time out.
+ ASSERT_TRUE(Gather(kDefaultTimeout * 3));
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectNatBlocksUDP) {
+ if (turn_server_.empty())
+ return;
+
+ UseNat();
+ BlockUdp();
+ AddStream(1);
+ std::vector<NrIceTurnServer> turn_servers;
+ std::vector<unsigned char> password_vec(turn_password_.begin(),
+ turn_password_.end());
+ turn_servers.push_back(
+ *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort,
+ turn_user_, password_vec, kNrIceTransportTcp));
+ turn_servers.push_back(
+ *NrIceTurnServer::Create(turn_server_, kDefaultStunServerPort,
+ turn_user_, password_vec, kNrIceTransportUdp));
+ SetTurnServers(turn_servers);
+ p1_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED,
+ kNrIceTransportTcp);
+ p2_->SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED,
+ kNrIceTransportTcp);
+ ASSERT_TRUE(Gather(kDefaultTimeout * 3));
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTwoComponents) {
+ AddStream(2);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTwoComponentsDisableSecond) {
+ AddStream(2);
+ ASSERT_TRUE(Gather());
+ p1_->DisableComponent(0, 2);
+ p2_->DisableComponent(0, 2);
+ Connect();
+}
+
+
+TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectP2();
+ PR_Sleep(1000);
+ ConnectP1();
+ WaitForConnectedStreams();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1Trickle) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectP2();
+ PR_Sleep(1000);
+ ConnectP1(TRICKLE_SIMULATE);
+ SimulateTrickleP1(0);
+ WaitForConnectedStreams();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1TrickleTwoComponents) {
+ AddStream(1);
+ AddStream(2);
+ ASSERT_TRUE(Gather());
+ ConnectP2();
+ PR_Sleep(1000);
+ ConnectP1(TRICKLE_SIMULATE);
+ SimulateTrickleP1(0);
+ std::cerr << "Sleeping between trickle streams" << std::endl;
+ PR_Sleep(1000); // Give this some time to settle but not complete
+ // all of ICE.
+ SimulateTrickleP1(1);
+ WaitForConnectedStreams(2);
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectAutoPrioritize) {
+ Init(false, false);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTrickleOneStreamOneComponent) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ SimulateTrickle(0);
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTrickleTwoStreamsOneComponent) {
+ AddStream(1);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ SimulateTrickle(0);
+ SimulateTrickle(1);
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+void RealisticTrickleDelay(
+ std::vector<SchedulableTrickleCandidate*>& candidates) {
+ for (size_t i = 0; i < candidates.size(); ++i) {
+ SchedulableTrickleCandidate* cand = candidates[i];
+ if (cand->IsHost()) {
+ cand->Schedule(i*10);
+ } else if (cand->IsReflexive()) {
+ cand->Schedule(i*10 + 100);
+ } else if (cand->IsRelay()) {
+ cand->Schedule(i*10 + 200);
+ }
+ }
+}
+
+void DelayRelayCandidates(
+ std::vector<SchedulableTrickleCandidate*>& candidates,
+ unsigned int ms) {
+ for (auto i = candidates.begin(); i != candidates.end(); ++i) {
+ if ((*i)->IsRelay()) {
+ (*i)->Schedule(ms);
+ } else {
+ (*i)->Schedule(0);
+ }
+ }
+}
+
+void AddNonPairableCandidates(
+ std::vector<SchedulableTrickleCandidate*>& candidates,
+ IceTestPeer *peer, size_t stream, int net_type,
+ MtransportTestUtils* test_utils_) {
+ for (int i=1; i<5; i++) {
+ if (net_type == i)
+ continue;
+ switch (i) {
+ case 1:
+ candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+ "candidate:0 1 UDP 2113601790 10.0.0.1 12345 typ host",
+ test_utils_));
+ break;
+ case 2:
+ candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+ "candidate:0 1 UDP 2113601791 172.16.1.1 12345 typ host",
+ test_utils_));
+ break;
+ case 3:
+ candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+ "candidate:0 1 UDP 2113601792 192.168.0.1 12345 typ host",
+ test_utils_));
+ break;
+ case 4:
+ candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+ "candidate:0 1 UDP 2113601793 100.64.1.1 12345 typ host",
+ test_utils_));
+ break;
+ default:
+ UNIMPLEMENTED;
+ }
+ }
+
+ for (auto i = candidates.rbegin(); i != candidates.rend(); ++i) {
+ std::cerr << "Scheduling candidate: " << (*i)->Candidate().c_str() << std::endl;
+ (*i)->Schedule(0);
+ }
+}
+
+void DropTrickleCandidates(
+ std::vector<SchedulableTrickleCandidate*>& candidates) {
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTrickleAddStreamDuringICE) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+ AddStream(1);
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTrickleAddStreamAfterICE) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+ WaitForConnected(1000);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, RemoveStream) {
+ AddStream(1);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ WaitForConnected(1000);
+
+ RemoveStream(0);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+}
+
+TEST_F(WebRtcIceConnectTest, P1NoTrickle) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ DropTrickleCandidates(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, P2NoTrickle) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ DropTrickleCandidates(p2_->ControlTrickle(0));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, RemoveAndAddStream) {
+ AddStream(1);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ WaitForConnected(1000);
+
+ RemoveStream(0);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(2));
+ RealisticTrickleDelay(p2_->ControlTrickle(2));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, RemoveStreamBeforeGather) {
+ AddStream(1);
+ AddStream(1);
+ ASSERT_TRUE(Gather(0));
+ RemoveStream(0);
+ WaitForGather();
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, RemoveStreamDuringGather) {
+ AddStream(1);
+ AddStream(1);
+ RemoveStream(0);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, RemoveStreamDuringConnect) {
+ AddStream(1);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ RemoveStream(0);
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectRealTrickleOneStreamOneComponent) {
+ AddStream(1);
+ AddStream(1);
+ ASSERT_TRUE(Gather(0));
+ ConnectTrickle(TRICKLE_REAL);
+ WaitForConnected();
+ WaitForGather(); // ICE can complete before we finish gathering.
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestSendReceive) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestSendReceiveTcp) {
+ Init(false, true);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsTcpCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
+ Connect();
+ SendReceive();
+}
+
+//TCP SO tests works on localhost only with delay applied:
+// tc qdisc add dev lo root netem delay 10ms
+TEST_F(WebRtcIceConnectTest, DISABLED_TestSendReceiveTcpSo) {
+ Init(false, true);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsTcpSoCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsent) {
+ AddStream(1);
+ SetupAndCheckConsent();
+ PR_Sleep(1500);
+ AssertConsentRefresh();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsentTcp) {
+ Init(false, true);
+ AddStream(1);
+ SetCandidateFilter(IsTcpCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
+ SetupAndCheckConsent();
+ PR_Sleep(1500);
+ AssertConsentRefresh();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsentIntermittent) {
+ AddStream(1);
+ SetupAndCheckConsent();
+ p1_->SetBlockStun(true);
+ p2_->SetBlockStun(true);
+ WaitForDisconnected();
+ AssertConsentRefresh(CONSENT_STALE);
+ SendReceive();
+ p1_->SetBlockStun(false);
+ p2_->SetBlockStun(false);
+ WaitForConnected();
+ AssertConsentRefresh();
+ SendReceive();
+ p1_->SetBlockStun(true);
+ p2_->SetBlockStun(true);
+ WaitForDisconnected();
+ AssertConsentRefresh(CONSENT_STALE);
+ SendReceive();
+ p1_->SetBlockStun(false);
+ p2_->SetBlockStun(false);
+ WaitForConnected();
+ AssertConsentRefresh();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsentTimeout) {
+ AddStream(1);
+ SetupAndCheckConsent();
+ p1_->SetBlockStun(true);
+ p2_->SetBlockStun(true);
+ WaitForDisconnected();
+ AssertConsentRefresh(CONSENT_STALE);
+ SendReceive();
+ WaitForFailed();
+ AssertConsentRefresh(CONSENT_EXPIRED);
+ SendFailure();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsentDelayed) {
+ AddStream(1);
+ SetupAndCheckConsent();
+ /* Note: We don't have a list of STUN transaction IDs of the previously timed
+ out consent requests. Thus responses after sending the next consent
+ request are ignored. */
+ p1_->SetStunResponseDelay(300);
+ p2_->SetStunResponseDelay(300);
+ PR_Sleep(1000);
+ AssertConsentRefresh();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurn) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithDelay) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ SetCandidateFilter(SabotageHostCandidateAndDropReflexive);
+ p1_->Gather();
+ PR_Sleep(500);
+ p2_->Gather();
+ ConnectTrickle(TRICKLE_REAL);
+ WaitForGather();
+ WaitForConnectedStreams();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelay) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+
+ WaitForConnected();
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelayOneSided) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ p2_->SimulateTrickle(0);
+
+ WaitForConnected();
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithLargeTrickleDelay) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ SetCandidateFilter(SabotageHostCandidateAndDropReflexive);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ // Trickle host candidates immediately, but delay relay candidates
+ DelayRelayCandidates(p1_->ControlTrickle(0), 3700);
+ DelayRelayCandidates(p2_->ControlTrickle(0), 3700);
+
+ WaitForConnected();
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnTcp) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_, kNrIceTransportTcp);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnOnly) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsRelayCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED);
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnTcpOnly) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_, kNrIceTransportTcp);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsRelayCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED,
+ kNrIceTransportTcp);
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestSendReceiveTurnOnly) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsRelayCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED);
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestSendReceiveTurnTcpOnly) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ SetTurnServer(turn_server_, kDefaultStunServerPort,
+ turn_user_, turn_password_, kNrIceTransportTcp);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsRelayCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED,
+ kNrIceTransportTcp);
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestSendReceiveTurnBothOnly) {
+ if (turn_server_.empty())
+ return;
+
+ AddStream(1);
+ std::vector<NrIceTurnServer> turn_servers;
+ std::vector<unsigned char> password_vec(turn_password_.begin(),
+ turn_password_.end());
+ turn_servers.push_back(*NrIceTurnServer::Create(
+ turn_server_, kDefaultStunServerPort,
+ turn_user_, password_vec, kNrIceTransportTcp));
+ turn_servers.push_back(*NrIceTurnServer::Create(
+ turn_server_, kDefaultStunServerPort,
+ turn_user_, password_vec, kNrIceTransportUdp));
+ SetTurnServers(turn_servers);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsRelayCandidate);
+ // UDP is preferred.
+ SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED,
+ kNrIceTransportUdp);
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectShutdownOneSide) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectThenDelete();
+}
+
+TEST_F(WebRtcIceConnectTest, TestPollCandPairsBeforeConnect) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+
+ std::vector<NrIceCandidatePair> pairs;
+ nsresult res = p1_->GetCandidatePairs(0, &pairs);
+ // There should be no candidate pairs prior to calling Connect()
+ ASSERT_EQ(NS_OK, res);
+ ASSERT_EQ(0U, pairs.size());
+
+ res = p2_->GetCandidatePairs(0, &pairs);
+ ASSERT_EQ(NS_OK, res);
+ ASSERT_EQ(0U, pairs.size());
+}
+
+TEST_F(WebRtcIceConnectTest, TestPollCandPairsAfterConnect) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+
+ std::vector<NrIceCandidatePair> pairs;
+ nsresult r = p1_->GetCandidatePairs(0, &pairs);
+ ASSERT_EQ(NS_OK, r);
+ // How detailed of a check do we want to do here? If the turn server is
+ // functioning, we'll get at least two pairs, but this is probably not
+ // something we should assume.
+ ASSERT_NE(0U, pairs.size());
+ ASSERT_TRUE(p1_->CandidatePairsPriorityDescending(pairs));
+ ASSERT_TRUE(ContainsSucceededPair(pairs));
+ pairs.clear();
+
+ r = p2_->GetCandidatePairs(0, &pairs);
+ ASSERT_EQ(NS_OK, r);
+ ASSERT_NE(0U, pairs.size());
+ ASSERT_TRUE(p2_->CandidatePairsPriorityDescending(pairs));
+ ASSERT_TRUE(ContainsSucceededPair(pairs));
+}
+
+// TODO Bug 1259842 - disabled until we find a better way to handle two
+// candidates from different RFC1918 ranges
+TEST_F(WebRtcIceConnectTest, DISABLED_TestHostCandPairingFilter) {
+ Init(false, false, false);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsIpv4Candidate);
+
+ int host_net = p1_->GetCandidatesPrivateIpv4Range(0);
+ if (host_net <= 0) {
+ // TODO bug 1226838: make this work with multiple private IPs
+ FAIL() << "This test needs exactly one private IPv4 host candidate to work" << std::endl;
+ }
+
+ ConnectTrickle();
+ AddNonPairableCandidates(p1_->ControlTrickle(0), p1_.get(), 0, host_net, test_utils_);
+ AddNonPairableCandidates(p2_->ControlTrickle(0), p2_.get(), 0, host_net, test_utils_);
+
+ std::vector<NrIceCandidatePair> pairs;
+ p1_->GetCandidatePairs(0, &pairs);
+ for (auto p : pairs) {
+ std::cerr << "Verifying pair:" << std::endl;
+ p1_->DumpCandidatePair(p);
+ nr_transport_addr addr;
+ nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+ ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net);
+ nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+ ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net);
+ }
+}
+
+// TODO Bug 1226838 - See Comment 2 - this test can't work as written
+TEST_F(WebRtcIceConnectTest, DISABLED_TestSrflxCandPairingFilter) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ Init(false, false, false);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsSrflxCandidate);
+
+ if (p1_->GetCandidatesPrivateIpv4Range(0) <= 0) {
+ // TODO bug 1226838: make this work with public IP addresses
+ std::cerr << "Don't run this test at IETF meetings!" << std::endl;
+ FAIL() << "This test needs one private IPv4 host candidate to work" << std::endl;
+ }
+
+ ConnectTrickle();
+ SimulateTrickleP1(0);
+ SimulateTrickleP2(0);
+
+ std::vector<NrIceCandidatePair> pairs;
+ p1_->GetCandidatePairs(0, &pairs);
+ for (auto p : pairs) {
+ std::cerr << "Verifying P1 pair:" << std::endl;
+ p1_->DumpCandidatePair(p);
+ nr_transport_addr addr;
+ nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+ ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0);
+ nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+ ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0);
+ }
+ p2_->GetCandidatePairs(0, &pairs);
+ for (auto p : pairs) {
+ std::cerr << "Verifying P2 pair:" << std::endl;
+ p2_->DumpCandidatePair(p);
+ nr_transport_addr addr;
+ nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+ ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0);
+ nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+ ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0);
+ }
+}
+
+TEST_F(WebRtcIceConnectTest, TestPollCandPairsDuringConnect) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+
+ p2_->Connect(p1_.get(), TRICKLE_NONE, false);
+ p1_->Connect(p2_.get(), TRICKLE_NONE, false);
+
+ std::vector<NrIceCandidatePair> pairs1;
+ std::vector<NrIceCandidatePair> pairs2;
+
+ p1_->StartChecks();
+ p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
+ p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
+
+ p2_->StartChecks();
+ p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
+ p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
+
+ WaitForConnectedStreams();
+ p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
+ p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
+ ASSERT_TRUE(ContainsSucceededPair(pairs1));
+ ASSERT_TRUE(ContainsSucceededPair(pairs2));
+}
+
+TEST_F(WebRtcIceConnectTest, TestRLogConnector) {
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+
+ p2_->Connect(p1_.get(), TRICKLE_NONE, false);
+ p1_->Connect(p2_.get(), TRICKLE_NONE, false);
+
+ std::vector<NrIceCandidatePair> pairs1;
+ std::vector<NrIceCandidatePair> pairs2;
+
+ p1_->StartChecks();
+ p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
+ p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
+
+ p2_->StartChecks();
+ p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
+ p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
+
+ WaitForConnectedStreams();
+ p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
+ p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
+ ASSERT_TRUE(ContainsSucceededPair(pairs1));
+ ASSERT_TRUE(ContainsSucceededPair(pairs2));
+
+ for (auto p = pairs1.begin(); p != pairs1.end(); ++p) {
+ std::deque<std::string> logs;
+ std::string substring("CAND-PAIR(");
+ substring += p->codeword;
+ RLogConnector::GetInstance()->Filter(substring, 0, &logs);
+ ASSERT_NE(0U, logs.size());
+ }
+
+ for (auto p = pairs2.begin(); p != pairs2.end(); ++p) {
+ std::deque<std::string> logs;
+ std::string substring("CAND-PAIR(");
+ substring += p->codeword;
+ RLogConnector::GetInstance()->Filter(substring, 0, &logs);
+ ASSERT_NE(0U, logs.size());
+ }
+}
+
+TEST_F(WebRtcIcePrioritizerTest, TestPrioritizer) {
+ SetPriorizer(::mozilla::CreateInterfacePrioritizer());
+
+ AddInterface("0", NR_INTERFACE_TYPE_VPN, 100); // unknown vpn
+ AddInterface("1", NR_INTERFACE_TYPE_VPN | NR_INTERFACE_TYPE_WIRED, 100); // wired vpn
+ AddInterface("2", NR_INTERFACE_TYPE_VPN | NR_INTERFACE_TYPE_WIFI, 100); // wifi vpn
+ AddInterface("3", NR_INTERFACE_TYPE_VPN | NR_INTERFACE_TYPE_MOBILE, 100); // wifi vpn
+ AddInterface("4", NR_INTERFACE_TYPE_WIRED, 1000); // wired, high speed
+ AddInterface("5", NR_INTERFACE_TYPE_WIRED, 10); // wired, low speed
+ AddInterface("6", NR_INTERFACE_TYPE_WIFI, 10); // wifi, low speed
+ AddInterface("7", NR_INTERFACE_TYPE_WIFI, 1000); // wifi, high speed
+ AddInterface("8", NR_INTERFACE_TYPE_MOBILE, 10); // mobile, low speed
+ AddInterface("9", NR_INTERFACE_TYPE_MOBILE, 1000); // mobile, high speed
+ AddInterface("10", NR_INTERFACE_TYPE_UNKNOWN, 10); // unknown, low speed
+ AddInterface("11", NR_INTERFACE_TYPE_UNKNOWN, 1000); // unknown, high speed
+
+ // expected preference "4" > "5" > "1" > "7" > "6" > "2" > "9" > "8" > "3" > "11" > "10" > "0"
+
+ HasLowerPreference("0", "10");
+ HasLowerPreference("10", "11");
+ HasLowerPreference("11", "3");
+ HasLowerPreference("3", "8");
+ HasLowerPreference("8", "9");
+ HasLowerPreference("9", "2");
+ HasLowerPreference("2", "6");
+ HasLowerPreference("6", "7");
+ HasLowerPreference("7", "1");
+ HasLowerPreference("1", "5");
+ HasLowerPreference("5", "4");
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestSendNonStunPacket) {
+ const unsigned char data[] = "12345abcde";
+ TestOutgoing(data, sizeof(data), 123, 45, false);
+ TestOutgoingTcp(data, sizeof(data), false);
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvNonStunPacket) {
+ const unsigned char data[] = "12345abcde";
+ TestIncoming(data, sizeof(data), 123, 45, false);
+ TestIncomingTcp(data, sizeof(data), true);
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestSendStunPacket) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ TestOutgoingTcp(msg->buffer, msg->length, true);
+ TestOutgoingTcpFramed(msg->buffer, msg->length, true);
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingId) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+ msg->header.id.octet[0] = 1;
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ TestOutgoingTcp(msg->buffer, msg->length, true);
+
+ msg->header.id.octet[0] = 0;
+ msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestIncoming(msg->buffer, msg->length, 123, 45, true);
+ TestIncomingTcp(msg->buffer, msg->length, true);
+
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingIdTcpFramed) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+ msg->header.id.octet[0] = 1;
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoingTcpFramed(msg->buffer, msg->length, true);
+
+ msg->header.id.octet[0] = 0;
+ msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestIncomingTcpFramed(msg->buffer, msg->length, true);
+
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingAddress) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ // nothing to test here for the TCP filter
+
+ msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestIncoming(msg->buffer, msg->length, 123, 46, false);
+ TestIncoming(msg->buffer, msg->length, 124, 45, false);
+
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithPendingIdAndAddress) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ TestOutgoingTcp(msg->buffer, msg->length, true);
+
+ msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestIncoming(msg->buffer, msg->length, 123, 45, true);
+ TestIncomingTcp(msg->buffer, msg->length, true);
+
+ // Test whitelist by filtering non-stun packets.
+ const unsigned char data[] = "12345abcde";
+
+ // 123:45 is white-listed.
+ TestOutgoing(data, sizeof(data), 123, 45, true);
+ TestOutgoingTcp(data, sizeof(data), true);
+ TestIncoming(data, sizeof(data), 123, 45, true);
+ TestIncomingTcp(data, sizeof(data), true);
+
+ // Indications pass as well.
+ msg->header.type = NR_STUN_MSG_BINDING_INDICATION;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ TestOutgoingTcp(msg->buffer, msg->length, true);
+ TestIncoming(msg->buffer, msg->length, 123, 45, true);
+ TestIncomingTcp(msg->buffer, msg->length, true);
+
+ // Packets from and to other address are still disallowed.
+ // Note: this doesn't apply for TCP connections
+ TestOutgoing(data, sizeof(data), 123, 46, false);
+ TestIncoming(data, sizeof(data), 123, 46, false);
+ TestOutgoing(data, sizeof(data), 124, 45, false);
+ TestIncoming(data, sizeof(data), 124, 45, false);
+
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithPendingIdTcpFramed) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoingTcpFramed(msg->buffer, msg->length, true);
+
+ msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestIncomingTcpFramed(msg->buffer, msg->length, true);
+
+ // Test whitelist by filtering non-stun packets.
+ const unsigned char data[] = "12345abcde";
+
+ TestOutgoingTcpFramed(data, sizeof(data), true);
+ TestIncomingTcpFramed(data, sizeof(data), true);
+
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestSendNonRequestStunPacket) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+ msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, false);
+ TestOutgoingTcp(msg->buffer, msg->length, false);
+
+ // Send a packet so we allow the incoming request.
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ TestOutgoingTcp(msg->buffer, msg->length, true);
+
+ // This packet makes us able to send a response.
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestIncoming(msg->buffer, msg->length, 123, 45, true);
+ TestIncomingTcp(msg->buffer, msg->length, true);
+
+ msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ TestOutgoingTcp(msg->buffer, msg->length, true);
+
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvDataPacketWithAPendingAddress) {
+ nr_stun_message *msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+ msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+ ASSERT_EQ(0, nr_stun_encode_message(msg));
+ TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+ TestOutgoingTcp(msg->buffer, msg->length, true);
+
+ const unsigned char data[] = "12345abcde";
+ TestIncoming(data, sizeof(data), 123, 45, true);
+ TestIncomingTcp(data, sizeof(data), true);
+
+ ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST(WebRtcIceInternalsTest, TestAddBogusAttribute) {
+ nr_stun_message *req;
+ ASSERT_EQ(0, nr_stun_message_create(&req));
+ Data *data;
+ ASSERT_EQ(0, r_data_alloc(&data, 3000));
+ memset(data->data, 'A', data->len);
+ ASSERT_TRUE(nr_stun_message_add_message_integrity_attribute(req, data));
+ ASSERT_EQ(0, r_data_destroy(&data));
+ ASSERT_EQ(0, nr_stun_message_destroy(&req));
+}
diff --git a/media/mtransport/test/moz.build b/media/mtransport/test/moz.build
new file mode 100644
index 000000000..e8f453787
--- /dev/null
+++ b/media/mtransport/test/moz.build
@@ -0,0 +1,100 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
+ SOURCES += [
+ 'buffered_stun_socket_unittest.cpp',
+ 'ice_unittest.cpp',
+ 'multi_tcp_socket_unittest.cpp',
+ 'nrappkit_unittest.cpp',
+ 'proxy_tunnel_socket_unittest.cpp',
+ 'rlogconnector_unittest.cpp',
+ 'runnable_utils_unittest.cpp',
+ 'simpletokenbucket_unittest.cpp',
+ 'sockettransportservice_unittest.cpp',
+ 'stunserver.cpp',
+ 'test_nr_socket_unittest.cpp',
+ 'TestSyncRunnable.cpp',
+ 'transport_unittests.cpp',
+ 'turn_unittest.cpp',
+ ]
+
+ if CONFIG['MOZ_SCTP']:
+ SOURCES += [
+ 'sctp_unittest.cpp',
+ ]
+
+
+for var in ('HAVE_STRDUP', 'NR_SOCKET_IS_VOID_PTR', 'SCTP_DEBUG', 'INET'):
+ DEFINES[var] = True
+
+if CONFIG['OS_TARGET'] == 'Android':
+ DEFINES['LINUX'] = True
+ DEFINES['ANDROID'] = True
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/android/include',
+ ]
+else:
+ DEFINES['INET6'] = True
+
+if CONFIG['OS_TARGET'] == 'Linux':
+ DEFINES['LINUX'] = True
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/linux/include',
+ ]
+
+if CONFIG['OS_TARGET'] == 'Darwin':
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/darwin/include',
+ ]
+
+if CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD'):
+ if CONFIG['OS_TARGET'] == 'Darwin':
+ DEFINES['DARWIN'] = True
+ else:
+ DEFINES['BSD'] = True
+ LOCAL_INCLUDES += [
+ '/media/mtransport/third_party/nrappkit/src/port/darwin/include',
+ ]
+
+# SCTP DEFINES
+if CONFIG['OS_TARGET'] == 'WINNT':
+ DEFINES['WIN'] = True
+ # for stun.h
+ DEFINES['WIN32'] = True
+ DEFINES['__Userspace_os_Windows'] = 1
+else:
+ # Works for Darwin, Linux, Android. Probably doesn't work for others.
+ DEFINES['__Userspace_os_%s' % CONFIG['OS_TARGET']] = 1
+
+if CONFIG['OS_TARGET'] in ('Darwin', 'Android'):
+ DEFINES['GTEST_USE_OWN_TR1_TUPLE'] = 1
+
+LOCAL_INCLUDES += [
+ '/media/mtransport/',
+ '/media/mtransport/third_party/',
+ '/media/mtransport/third_party/nICEr/src/crypto',
+ '/media/mtransport/third_party/nICEr/src/ice',
+ '/media/mtransport/third_party/nICEr/src/net',
+ '/media/mtransport/third_party/nICEr/src/stun',
+ '/media/mtransport/third_party/nICEr/src/util',
+ '/media/mtransport/third_party/nrappkit/src/event',
+ '/media/mtransport/third_party/nrappkit/src/log',
+ '/media/mtransport/third_party/nrappkit/src/plugin',
+ '/media/mtransport/third_party/nrappkit/src/port/generic/include',
+ '/media/mtransport/third_party/nrappkit/src/registry',
+ '/media/mtransport/third_party/nrappkit/src/share',
+ '/media/mtransport/third_party/nrappkit/src/stats',
+ '/media/mtransport/third_party/nrappkit/src/util/',
+ '/media/mtransport/third_party/nrappkit/src/util/libekr',
+ '/netwerk/sctp/src/',
+ '/xpcom/tests/'
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/media/mtransport/test/mtransport_test_utils.h b/media/mtransport/test/mtransport_test_utils.h
new file mode 100644
index 000000000..81c16ff83
--- /dev/null
+++ b/media/mtransport/test/mtransport_test_utils.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef mtransport_test_utils_h__
+#define mtransport_test_utils_h__
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+
+#include "nsIEventTarget.h"
+#include "nsPISocketTransportService.h"
+#include "nsServiceManagerUtils.h"
+
+class MtransportTestUtils {
+ public:
+ MtransportTestUtils() {
+ InitServices();
+ }
+
+ ~MtransportTestUtils() {
+ }
+
+ void InitServices() {
+ nsresult rv;
+ sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ sts_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ nsIEventTarget* sts_target() { return sts_target_; }
+
+ private:
+ nsCOMPtr<nsIEventTarget> sts_target_;
+ nsCOMPtr<nsPISocketTransportService> sts_;
+};
+
+
+#define CHECK_ENVIRONMENT_FLAG(envname) \
+ char *test_flag = getenv(envname); \
+ if (!test_flag || strcmp(test_flag, "1")) { \
+ printf("To run this test set %s=1 in your environment\n", envname); \
+ exit(0); \
+ } \
+
+
+#endif
diff --git a/media/mtransport/test/multi_tcp_socket_unittest.cpp b/media/mtransport/test/multi_tcp_socket_unittest.cpp
new file mode 100644
index 000000000..7e513181f
--- /dev/null
+++ b/media/mtransport/test/multi_tcp_socket_unittest.cpp
@@ -0,0 +1,540 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <iostream>
+#include <vector>
+
+#include "mozilla/Atomics.h"
+#include "runnable_utils.h"
+#include "nss.h"
+#include "pk11pub.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "transport_addr.h"
+#include "ice_ctx.h"
+#include "nr_socket_multi_tcp.h"
+}
+
+#include "nr_socket_prsock.h"
+
+#include "stunserver.h"
+
+#include "nricectxhandler.h"
+#include "nricemediastream.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+namespace {
+
+class MultiTcpSocketTest : public MtransportTest {
+ public:
+ MultiTcpSocketTest()
+ :MtransportTest(),
+ socks(3,nullptr),
+ readable(false),
+ ice_ctx_()
+ {}
+
+ void SetUp() {
+ MtransportTest::SetUp();
+
+ ice_ctx_ = NrIceCtxHandler::Create("stun", true);
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET),
+ NS_DISPATCH_SYNC);
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(&TestStunTcpServer::GetInstance, AF_INET6),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void TearDown() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::Shutdown_s),
+ NS_DISPATCH_SYNC);
+
+ MtransportTest::TearDown();
+ }
+
+ DISALLOW_COPY_ASSIGN(MultiTcpSocketTest);
+
+ static void SockReadable(NR_SOCKET s, int how, void *arg) {
+ MultiTcpSocketTest *obj=static_cast<MultiTcpSocketTest *>(arg);
+ obj->SetReadable(true);
+ }
+
+ void Shutdown_s() {
+ ice_ctx_ = nullptr;
+ for (std::vector<nr_socket *>::iterator it=socks.begin();
+ it!=socks.end(); ++it) {
+ nr_socket_destroy(&(*it));
+ }
+ }
+
+ static uint16_t GetRandomPort() {
+ uint16_t result;
+ if (PK11_GenerateRandom((unsigned char*)&result, 2) != SECSuccess) {
+ MOZ_ASSERT(false);
+ return 0;
+ }
+ return result;
+ }
+
+ static uint16_t EnsureEphemeral(uint16_t port) {
+ // IANA ephemeral port range (49152 to 65535)
+ return port | 49152;
+ }
+
+ void Create_s(nr_socket_tcp_type tcp_type, std::string stun_server_addr,
+ uint16_t stun_server_port, nr_socket **sock) {
+ nr_transport_addr local;
+ // Get start of port range for test
+ static unsigned short port_s = GetRandomPort();
+ int r;
+
+ if (!stun_server_addr.empty()) {
+ std::vector<NrIceStunServer> stun_servers;
+ UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(
+ stun_server_addr, stun_server_port, kNrIceTransportTcp));
+ stun_servers.push_back(*server);
+
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetStunServers(stun_servers)));
+ }
+
+ r = 1;
+ for (int tries=10; tries && r; --tries) {
+ r = nr_str_port_to_transport_addr(
+ (char *)"127.0.0.1", EnsureEphemeral(port_s++), IPPROTO_TCP, &local);
+ ASSERT_EQ(0, r);
+
+ r = nr_socket_multi_tcp_create(ice_ctx_->ctx()->ctx(),
+ &local, tcp_type, 1, 2048, sock);
+ }
+
+ ASSERT_EQ(0, r);
+ printf("Creating socket on %s\n", local.as_string);
+ r = nr_socket_multi_tcp_set_readable_cb(*sock,
+ &MultiTcpSocketTest::SockReadable, this);
+ ASSERT_EQ(0, r);
+ }
+
+ nr_socket *Create(nr_socket_tcp_type tcp_type,
+ std::string stun_server_addr = "",
+ uint16_t stun_server_port = 0) {
+ nr_socket *sock=nullptr;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::Create_s, tcp_type,
+ stun_server_addr, stun_server_port, &sock),
+ NS_DISPATCH_SYNC);
+ return sock;
+ }
+
+ void Listen_s(nr_socket *sock) {
+ nr_transport_addr addr;
+ int r=nr_socket_getaddr(sock, &addr);
+ ASSERT_EQ(0, r);
+ printf("Listening on %s\n", addr.as_string);
+ r = nr_socket_listen(sock, 5);
+ ASSERT_EQ(0, r);
+ }
+
+ void Listen(nr_socket *sock) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::Listen_s, sock),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Destroy_s(nr_socket *sock) {
+ int r = nr_socket_destroy(&sock);
+ ASSERT_EQ(0, r);
+ }
+
+ void Destroy(nr_socket *sock) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::Destroy_s, sock),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Connect_s(nr_socket *from, nr_socket *to) {
+ nr_transport_addr addr_to;
+ nr_transport_addr addr_from;
+ int r=nr_socket_getaddr(to, &addr_to);
+ ASSERT_EQ(0, r);
+ r=nr_socket_getaddr(from, &addr_from);
+ ASSERT_EQ(0, r);
+ printf("Connecting from %s to %s\n", addr_from.as_string, addr_to.as_string);
+ r=nr_socket_connect(from, &addr_to);
+ ASSERT_EQ(0, r);
+ }
+
+ void Connect(nr_socket *from, nr_socket *to) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::Connect_s, from, to),
+ NS_DISPATCH_SYNC);
+ }
+
+ void ConnectSo_s(nr_socket *so1, nr_socket *so2) {
+ nr_transport_addr addr_so1;
+ nr_transport_addr addr_so2;
+ int r=nr_socket_getaddr(so1, &addr_so1);
+ ASSERT_EQ(0, r);
+ r=nr_socket_getaddr(so2, &addr_so2);
+ ASSERT_EQ(0, r);
+ printf("Connecting SO %s <-> %s\n", addr_so1.as_string, addr_so2.as_string);
+ r=nr_socket_connect(so1, &addr_so2);
+ ASSERT_EQ(0, r);
+ r=nr_socket_connect(so2, &addr_so1);
+ ASSERT_EQ(0, r);
+ }
+
+ void ConnectSo(nr_socket *from, nr_socket *to) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::ConnectSo_s, from, to),
+ NS_DISPATCH_SYNC);
+ }
+
+ void SendDataToAddress_s(nr_socket *from, nr_transport_addr *to, const char *data,
+ size_t len) {
+ nr_transport_addr addr_from;
+
+ int r=nr_socket_getaddr(from, &addr_from);
+ ASSERT_EQ(0, r);
+ printf("Sending %lu bytes %s -> %s\n", (unsigned long)len,
+ addr_from.as_string, to->as_string);
+ r=nr_socket_sendto(from, data, len, 0, to);
+ ASSERT_EQ(0, r);
+ }
+
+ void SendData(nr_socket *from, nr_transport_addr *to, const char *data, size_t len) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::SendDataToAddress_s, from, to, data,
+ len),
+ NS_DISPATCH_SYNC);
+ }
+
+ void SendDataToSocket_s(nr_socket *from, nr_socket *to, const char *data,
+ size_t len) {
+ nr_transport_addr addr_to;
+
+ int r=nr_socket_getaddr(to, &addr_to);
+ ASSERT_EQ(0, r);
+ SendDataToAddress_s(from, &addr_to, data, len);
+ }
+
+ void SendData(nr_socket *from, nr_socket *to, const char *data, size_t len) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::SendDataToSocket_s, from, to, data,
+ len),
+ NS_DISPATCH_SYNC);
+ }
+
+ void RecvDataFromAddress_s(nr_transport_addr *expected_from,
+ nr_socket *sent_to,
+ const char *expected_data,
+ size_t expected_len) {
+ SetReadable(false);
+ size_t buflen = expected_len ? expected_len+1 : 100;
+ char received_data[buflen];
+ nr_transport_addr addr_to;
+ nr_transport_addr retaddr;
+ size_t retlen;
+
+ int r=nr_socket_getaddr(sent_to, &addr_to);
+ ASSERT_EQ(0, r);
+ printf("Receiving %lu bytes %s <- %s\n", (unsigned long)expected_len,
+ addr_to.as_string, expected_from->as_string);
+ r=nr_socket_recvfrom(sent_to, received_data, buflen,
+ &retlen, 0, &retaddr);
+ ASSERT_EQ(0, r);
+ r=nr_transport_addr_cmp(&retaddr, expected_from,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL);
+ ASSERT_EQ(0, r);
+ // expected_len == 0 means we just expected some data
+ if (expected_len == 0) {
+ ASSERT_GT(retlen, 0U);
+ } else {
+ ASSERT_EQ(expected_len, retlen);
+ r=memcmp(expected_data, received_data, retlen);
+ ASSERT_EQ(0, r);
+ }
+ }
+
+ void RecvData(nr_transport_addr *expected_from, nr_socket *sent_to,
+ const char *expected_data = nullptr, size_t expected_len = 0) {
+ ASSERT_TRUE_WAIT(IsReadable(), 1000);
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::RecvDataFromAddress_s,
+ expected_from, sent_to, expected_data,
+ expected_len),
+ NS_DISPATCH_SYNC);
+ }
+
+ void RecvDataFromSocket_s(nr_socket *expected_from, nr_socket *sent_to,
+ const char *expected_data, size_t expected_len) {
+ nr_transport_addr addr_from;
+
+ int r=nr_socket_getaddr(expected_from, &addr_from);
+ ASSERT_EQ(0, r);
+
+ RecvDataFromAddress_s(&addr_from, sent_to, expected_data, expected_len);
+ }
+
+ void RecvData(nr_socket *expected_from, nr_socket *sent_to,
+ const char *expected_data, size_t expected_len) {
+ ASSERT_TRUE_WAIT(IsReadable(), 1000);
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::RecvDataFromSocket_s,
+ expected_from, sent_to, expected_data, expected_len),
+ NS_DISPATCH_SYNC);
+ }
+
+ void RecvDataFailed_s(nr_socket *sent_to, size_t expected_len, int expected_err) {
+ SetReadable(false);
+ char received_data[expected_len+1];
+ nr_transport_addr addr_to;
+ nr_transport_addr retaddr;
+ size_t retlen;
+
+ int r=nr_socket_getaddr(sent_to, &addr_to);
+ ASSERT_EQ(0, r);
+ r=nr_socket_recvfrom(sent_to, received_data, expected_len+1,
+ &retlen, 0, &retaddr);
+ ASSERT_EQ(expected_err, r) << "Expecting receive failure " << expected_err
+ << " on " << addr_to.as_string;
+ }
+
+ void RecvDataFailed(nr_socket *sent_to, size_t expected_len,
+ int expected_err) {
+ ASSERT_TRUE_WAIT(IsReadable(), 1000);
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(
+ this, &MultiTcpSocketTest::RecvDataFailed_s, sent_to, expected_len,
+ expected_err),
+ NS_DISPATCH_SYNC);
+ }
+
+ void TransferData(nr_socket *from, nr_socket *to, const char *data,
+ size_t len) {
+ SendData(from, to, data, len);
+ RecvData(from, to, data, len);
+ }
+
+ protected:
+ bool IsReadable() const {
+ return readable;
+ }
+ void SetReadable(bool r) {
+ readable=r;
+ }
+ std::vector<nr_socket *> socks;
+ Atomic<bool> readable;
+ RefPtr<NrIceCtxHandler> ice_ctx_;
+};
+}
+
+TEST_F(MultiTcpSocketTest, TestListen) {
+ socks[0] = Create(TCP_TYPE_PASSIVE);
+ Listen(socks[0]);
+}
+
+TEST_F(MultiTcpSocketTest, TestConnect) {
+ socks[0] = Create(TCP_TYPE_PASSIVE);
+ socks[1] = Create(TCP_TYPE_ACTIVE);
+ socks[2] = Create(TCP_TYPE_ACTIVE);
+ Listen(socks[0]);
+ Connect(socks[1], socks[0]);
+ Connect(socks[2], socks[0]);
+}
+
+TEST_F(MultiTcpSocketTest, TestTransmit) {
+ const char data[] = "TestTransmit";
+ socks[0] = Create(TCP_TYPE_ACTIVE);
+ socks[1] = Create(TCP_TYPE_PASSIVE);
+ Listen(socks[1]);
+ Connect(socks[0], socks[1]);
+
+ TransferData(socks[0], socks[1], data, sizeof(data));
+ TransferData(socks[1], socks[0], data, sizeof(data));
+}
+
+TEST_F(MultiTcpSocketTest, TestClosePassive) {
+ const char data[] = "TestClosePassive";
+ socks[0] = Create(TCP_TYPE_ACTIVE);
+ socks[1] = Create(TCP_TYPE_PASSIVE);
+ Listen(socks[1]);
+ Connect(socks[0], socks[1]);
+
+ TransferData(socks[0], socks[1], data, sizeof(data));
+ TransferData(socks[1], socks[0], data, sizeof(data));
+
+ /* We have to destroy as only that calls PR_Close() */
+ std::cerr << "Destructing socket" << std::endl;
+ Destroy(socks[1]);
+
+ RecvDataFailed(socks[0], sizeof(data), R_EOD);
+
+ socks[1] = nullptr;
+}
+
+TEST_F(MultiTcpSocketTest, TestCloseActive) {
+ const char data[] = "TestCloseActive";
+ socks[0] = Create(TCP_TYPE_ACTIVE);
+ socks[1] = Create(TCP_TYPE_PASSIVE);
+ Listen(socks[1]);
+ Connect(socks[0], socks[1]);
+
+ TransferData(socks[0], socks[1], data, sizeof(data));
+ TransferData(socks[1], socks[0], data, sizeof(data));
+
+ /* We have to destroy as only that calls PR_Close() */
+ std::cerr << "Destructing socket" << std::endl;
+ Destroy(socks[0]);
+
+ RecvDataFailed(socks[1], sizeof(data), R_EOD);
+
+ socks[0] = nullptr;
+}
+
+TEST_F(MultiTcpSocketTest, TestTwoSendsBeforeReceives) {
+ const char data1[] = "TestTwoSendsBeforeReceives";
+ const char data2[] = "2nd data";
+ socks[0] = Create(TCP_TYPE_ACTIVE);
+ socks[1] = Create(TCP_TYPE_PASSIVE);
+ Listen(socks[1]);
+ Connect(socks[0], socks[1]);
+
+ SendData(socks[0], socks[1], data1, sizeof(data1));
+ SendData(socks[0], socks[1], data2, sizeof(data2));
+ RecvData(socks[0], socks[1], data1, sizeof(data1));
+ /* ICE TCP framing turns TCP effectively into datagram mode */
+ RecvData(socks[0], socks[1], data2, sizeof(data2));
+}
+
+TEST_F(MultiTcpSocketTest, TestTwoActiveBidirectionalTransmit) {
+ const char data1[] = "TestTwoActiveBidirectionalTransmit";
+ const char data2[] = "ReplyToTheFirstSocket";
+ const char data3[] = "TestMessageFromTheSecondSocket";
+ const char data4[] = "ThisIsAReplyToTheSecondSocket";
+ socks[0] = Create(TCP_TYPE_PASSIVE);
+ socks[1] = Create(TCP_TYPE_ACTIVE);
+ socks[2] = Create(TCP_TYPE_ACTIVE);
+ Listen(socks[0]);
+ Connect(socks[1], socks[0]);
+ Connect(socks[2], socks[0]);
+
+ TransferData(socks[1], socks[0], data1, sizeof(data1));
+ TransferData(socks[0], socks[1], data2, sizeof(data2));
+ TransferData(socks[2], socks[0], data3, sizeof(data3));
+ TransferData(socks[0], socks[2], data4, sizeof(data4));
+}
+
+TEST_F(MultiTcpSocketTest, TestTwoPassiveBidirectionalTransmit) {
+ const char data1[] = "TestTwoPassiveBidirectionalTransmit";
+ const char data2[] = "FirstReply";
+ const char data3[] = "TestTwoPassiveBidirectionalTransmitToTheSecondSock";
+ const char data4[] = "SecondReply";
+ socks[0] = Create(TCP_TYPE_PASSIVE);
+ socks[1] = Create(TCP_TYPE_PASSIVE);
+ socks[2] = Create(TCP_TYPE_ACTIVE);
+ Listen(socks[0]);
+ Listen(socks[1]);
+ Connect(socks[2], socks[0]);
+ Connect(socks[2], socks[1]);
+
+ TransferData(socks[2], socks[0], data1, sizeof(data1));
+ TransferData(socks[0], socks[2], data2, sizeof(data2));
+ TransferData(socks[2], socks[1], data3, sizeof(data3));
+ TransferData(socks[1], socks[2], data4, sizeof(data4));
+}
+
+TEST_F(MultiTcpSocketTest, TestActivePassiveWithStunServerMockup) {
+ /* Fake STUN message able to pass the nr_is_stun_msg check
+ used in nr_socket_buffered_stun */
+ const char stunMessage[] = {
+ '\x00', '\x01', '\x00', '\x04', '\x21', '\x12', '\xa4', '\x42',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x0c', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x1c', '\xed', '\xca', '\xfe'
+ };
+ const char data[] = "TestActivePassiveWithStunServerMockup";
+
+ nr_transport_addr stun_srv_addr;
+ std::string stun_addr;
+ uint16_t stun_port;
+ stun_addr = TestStunTcpServer::GetInstance(AF_INET)->addr();
+ stun_port = TestStunTcpServer::GetInstance(AF_INET)->port();
+ int r = nr_str_port_to_transport_addr(stun_addr.c_str(), stun_port, IPPROTO_TCP, &stun_srv_addr);
+ ASSERT_EQ(0, r);
+
+ socks[0] = Create(TCP_TYPE_PASSIVE, stun_addr, stun_port);
+ Listen(socks[0]);
+ socks[1] = Create(TCP_TYPE_ACTIVE, stun_addr, stun_port);
+
+ /* Send a fake STUN request and expect a STUN error response */
+ SendData(socks[0], &stun_srv_addr, stunMessage, sizeof(stunMessage));
+ RecvData(&stun_srv_addr, socks[0]);
+
+ Connect(socks[1], socks[0]);
+ TransferData(socks[1], socks[0], data, sizeof(data));
+ TransferData(socks[0], socks[1], data, sizeof(data));
+}
+
+TEST_F(MultiTcpSocketTest, TestConnectTwoSo) {
+ socks[0] = Create(TCP_TYPE_SO);
+ socks[1] = Create(TCP_TYPE_SO);
+ ConnectSo(socks[0], socks[1]);
+}
+
+// test works on localhost only with delay applied:
+// tc qdisc add dev lo root netem delay 5ms
+TEST_F(MultiTcpSocketTest, DISABLED_TestTwoSoBidirectionalTransmit) {
+ const char data[] = "TestTwoSoBidirectionalTransmit";
+ socks[0] = Create(TCP_TYPE_SO);
+ socks[1] = Create(TCP_TYPE_SO);
+ ConnectSo(socks[0], socks[1]);
+ TransferData(socks[0], socks[1], data, sizeof(data));
+ TransferData(socks[1], socks[0], data, sizeof(data));
+}
+
+TEST_F(MultiTcpSocketTest, TestBigData) {
+ char buf1[2048];
+ char buf2[1024];
+
+ for(unsigned i=0; i<sizeof(buf1); ++i) {
+ buf1[i]=i&0xff;
+ }
+ for(unsigned i=0; i<sizeof(buf2); ++i) {
+ buf2[i]=(i+0x80)&0xff;
+ }
+ socks[0] = Create(TCP_TYPE_ACTIVE);
+ socks[1] = Create(TCP_TYPE_PASSIVE);
+ Listen(socks[1]);
+ Connect(socks[0], socks[1]);
+
+ TransferData(socks[0], socks[1], buf1, sizeof(buf1));
+ TransferData(socks[0], socks[1], buf2, sizeof(buf2));
+// opposite dir
+ SendData(socks[1], socks[0], buf2, sizeof(buf2));
+ SendData(socks[1], socks[0], buf1, sizeof(buf1));
+ RecvData(socks[1], socks[0], buf2, sizeof(buf2));
+ RecvData(socks[1], socks[0], buf1, sizeof(buf1));
+}
diff --git a/media/mtransport/test/nrappkit_unittest.cpp b/media/mtransport/test/nrappkit_unittest.cpp
new file mode 100644
index 000000000..48f904758
--- /dev/null
+++ b/media/mtransport/test/nrappkit_unittest.cpp
@@ -0,0 +1,134 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+#include <iostream>
+
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+// nrappkit includes
+extern "C" {
+#include "nr_api.h"
+#include "async_timer.h"
+}
+
+#include "runnable_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+namespace {
+
+class TimerTest : public MtransportTest {
+ public:
+ TimerTest() : MtransportTest(), handle_(nullptr), fired_(false) {}
+ virtual ~TimerTest() {}
+
+ int ArmTimer(int timeout) {
+ int ret;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&ret, this, &TimerTest::ArmTimer_w, timeout),
+ NS_DISPATCH_SYNC);
+
+ return ret;
+ }
+
+ int ArmCancelTimer(int timeout) {
+ int ret;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&ret, this, &TimerTest::ArmCancelTimer_w, timeout),
+ NS_DISPATCH_SYNC);
+
+ return ret;
+ }
+
+ int ArmTimer_w(int timeout) {
+ return NR_ASYNC_TIMER_SET(timeout, cb, this, &handle_);
+ }
+
+ int ArmCancelTimer_w(int timeout) {
+ int r;
+ r = ArmTimer_w(timeout);
+ if (r)
+ return r;
+
+ return CancelTimer_w();
+ }
+
+ int CancelTimer() {
+ int ret;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&ret, this, &TimerTest::CancelTimer_w),
+ NS_DISPATCH_SYNC);
+
+ return ret;
+ }
+
+ int CancelTimer_w() {
+ return NR_async_timer_cancel(handle_);
+ }
+
+ int Schedule() {
+ int ret;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&ret, this, &TimerTest::Schedule_w),
+ NS_DISPATCH_SYNC);
+
+ return ret;
+ }
+
+ int Schedule_w() {
+ NR_ASYNC_SCHEDULE(cb, this);
+
+ return 0;
+ }
+
+
+ static void cb(NR_SOCKET r, int how, void *arg) {
+ std::cerr << "Timer fired " << std::endl;
+
+ TimerTest *t = static_cast<TimerTest *>(arg);
+
+ t->fired_ = true;
+ }
+
+ protected:
+ void *handle_;
+ bool fired_;
+};
+}
+
+TEST_F(TimerTest, SimpleTimer) {
+ ArmTimer(100);
+ ASSERT_TRUE_WAIT(fired_, 1000);
+}
+
+TEST_F(TimerTest, CancelTimer) {
+ ArmTimer(1000);
+ CancelTimer();
+ PR_Sleep(2000);
+ ASSERT_FALSE(fired_);
+}
+
+TEST_F(TimerTest, CancelTimer0) {
+ ArmCancelTimer(0);
+ PR_Sleep(100);
+ ASSERT_FALSE(fired_);
+}
+
+TEST_F(TimerTest, ScheduleTest) {
+ Schedule();
+ ASSERT_TRUE_WAIT(fired_, 1000);
+}
diff --git a/media/mtransport/test/proxy_tunnel_socket_unittest.cpp b/media/mtransport/test/proxy_tunnel_socket_unittest.cpp
new file mode 100644
index 000000000..facd0c04c
--- /dev/null
+++ b/media/mtransport/test/proxy_tunnel_socket_unittest.cpp
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original authors: ekr@rtfm.com; ryan@tokbox.com
+
+#include <iostream>
+
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "nr_proxy_tunnel.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "databuffer.h"
+#include "dummysocket.h"
+
+#include "nr_socket_prsock.h"
+#include "nriceresolverfake.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+const std::string kRemoteAddr = "192.0.2.133";
+const uint16_t kRemotePort = 3333;
+
+const std::string kProxyHost = "example.com";
+const std::string kProxyAddr = "192.0.2.134";
+const uint16_t kProxyPort = 9999;
+
+const std::string kHelloMessage = "HELLO";
+const std::string kGarbageMessage = "xxxxxxxxxx";
+
+std::string connect_message(const std::string &host, uint16_t port, const std::string &alpn, const std::string &tail) {
+ std::stringstream ss;
+ ss << "CONNECT " << host << ":" << port << " HTTP/1.0\r\n";
+ if (!alpn.empty()) {
+ ss << "ALPN: " << alpn << "\r\n";
+ }
+ ss << "\r\n" << tail;
+ return ss.str();
+}
+
+std::string connect_response(int code, const std::string &tail = "") {
+ std::stringstream ss;
+ ss << "HTTP/1.0 " << code << "\r\n\r\n" << tail;
+ return ss.str();
+}
+
+class DummyResolver {
+ public:
+ DummyResolver() {
+ vtbl_ = new nr_resolver_vtbl;
+ vtbl_->destroy = &DummyResolver::destroy;
+ vtbl_->resolve = &DummyResolver::resolve;
+ vtbl_->cancel = &DummyResolver::cancel;
+ nr_resolver_create_int((void *)this, vtbl_, &resolver_);
+ }
+
+ ~DummyResolver() {
+ nr_resolver_destroy(&resolver_);
+ delete vtbl_;
+ }
+
+ static int destroy(void **objp) {
+ return 0;
+ }
+
+ static int resolve(void *obj,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle) {
+ nr_transport_addr addr;
+
+ nr_str_port_to_transport_addr(
+ (char *)kProxyAddr.c_str(), kProxyPort, IPPROTO_TCP, &addr);
+
+ cb(cb_arg, &addr);
+ return 0;
+ }
+
+ static int cancel(void *obj, void *handle) {
+ return 0;
+ }
+
+ nr_resolver *get_nr_resolver() {
+ return resolver_;
+ }
+
+ private:
+ nr_resolver_vtbl *vtbl_;
+ nr_resolver *resolver_;
+};
+
+class ProxyTunnelSocketTest : public MtransportTest {
+ public:
+ ProxyTunnelSocketTest()
+ : socket_impl_(nullptr),
+ nr_socket_(nullptr) {}
+
+ ~ProxyTunnelSocketTest() {
+ nr_socket_destroy(&nr_socket_);
+ nr_proxy_tunnel_config_destroy(&config_);
+ }
+
+ void SetUp() override {
+ MtransportTest::SetUp();
+
+ nr_resolver_ = resolver_impl_.get_nr_resolver();
+
+ int r = nr_str_port_to_transport_addr(
+ (char *)kRemoteAddr.c_str(), kRemotePort, IPPROTO_TCP, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ r = nr_str_port_to_transport_addr(
+ (char *)kProxyAddr.c_str(), kProxyPort, IPPROTO_TCP, &proxy_addr_);
+ ASSERT_EQ(0, r);
+
+ nr_proxy_tunnel_config_create(&config_);
+ nr_proxy_tunnel_config_set_resolver(config_, nr_resolver_);
+ nr_proxy_tunnel_config_set_proxy(config_, kProxyAddr.c_str(), kProxyPort);
+
+ Configure();
+ }
+
+ // This reconfigures the socket with the updated information in config_.
+ void Configure() {
+ if (nr_socket_) {
+ EXPECT_EQ(0, nr_socket_destroy(&nr_socket_));
+ EXPECT_EQ(nullptr, nr_socket_);
+ }
+
+ RefPtr<DummySocket> dummy(new DummySocket());
+ int r = nr_socket_proxy_tunnel_create(
+ config_,
+ dummy->get_nr_socket(),
+ &nr_socket_);
+ ASSERT_EQ(0, r);
+
+ socket_impl_ = dummy.forget(); // Now owned by nr_socket_.
+ }
+
+ void Connect(int expectedReturn = 0) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ EXPECT_EQ(expectedReturn, r);
+
+ size_t written = 0;
+ r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(kHelloMessage.size(), written);
+ }
+
+ nr_socket *socket() { return nr_socket_; }
+
+ protected:
+ RefPtr<DummySocket> socket_impl_;
+ DummyResolver resolver_impl_;
+ nr_socket *nr_socket_;
+ nr_resolver *nr_resolver_;
+ nr_proxy_tunnel_config *config_;
+ nr_transport_addr proxy_addr_;
+ nr_transport_addr remote_addr_;
+};
+
+
+TEST_F(ProxyTunnelSocketTest, TestCreate) {
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyAddress) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ ASSERT_EQ(0, nr_transport_addr_cmp(socket_impl_->get_connect_addr(), &proxy_addr_,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyRequest) {
+ Connect();
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestAlpnConnect) {
+ const std::string alpn = "this,is,alpn";
+ int r = nr_proxy_tunnel_config_set_alpn(config_, alpn.c_str());
+ EXPECT_EQ(0, r);
+
+ Configure();
+ Connect();
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, alpn, kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestNullAlpnConnect) {
+ int r = nr_proxy_tunnel_config_set_alpn(config_, nullptr);
+ EXPECT_EQ(0, r);
+
+ Configure();
+ Connect();
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyHostRequest) {
+ nr_proxy_tunnel_config_set_proxy(config_, kProxyHost.c_str(), kProxyPort);
+ Configure();
+ // Because kProxyHost is a domain name and not an IP address,
+ // nr_socket_connect will need to resolve an IP address before continuing. It
+ // does that, and assumes that resolving the IP will take some time, so it
+ // returns R_WOULDBLOCK.
+ //
+ // However, In this test setup, the resolution happens inline immediately, so
+ // nr_socket_connect is called recursively on the inner socket in
+ // nr_socket_proxy_tunnel_resolved_cb. That also completes. Thus, the socket
+ // is actually successfully connected after this call, even though
+ // nr_socket_connect reports an error.
+ //
+ // Arguably nr_socket_proxy_tunnel_connect() is busted, because it shouldn't
+ // report an error when it doesn't need any further assistance from the
+ // calling code, but that's pretty minor.
+ Connect(R_WOULDBLOCK);
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyWrite) {
+ Connect();
+
+ socket_impl_->ClearWriteBuffer();
+
+ size_t written = 0;
+ int r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(kHelloMessage.size(), written);
+
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
+ kHelloMessage.size());
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxySuccessResponse) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(200, kHelloMessage);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+
+ ASSERT_EQ(kHelloMessage.size(), read);
+ ASSERT_EQ(0, memcmp(buf, kHelloMessage.c_str(), kHelloMessage.size()));
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyRead) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(200, kHelloMessage);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+
+ socket_impl_->ClearReadBuffer();
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
+ kHelloMessage.size());
+
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+
+ ASSERT_EQ(kHelloMessage.size(), read);
+ ASSERT_EQ(0, memcmp(buf, kHelloMessage.c_str(), kHelloMessage.size()));
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyReadNone) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(200);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(R_WOULDBLOCK, r);
+
+ socket_impl_->ClearReadBuffer();
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
+ kHelloMessage.size());
+
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyFailResponse) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(500, kHelloMessage);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_NE(0, r);
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyGarbageResponse) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kGarbageMessage.c_str()),
+ kGarbageMessage.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0ul, read);
+}
diff --git a/media/mtransport/test/rlogconnector_unittest.cpp b/media/mtransport/test/rlogconnector_unittest.cpp
new file mode 100644
index 000000000..e283e8199
--- /dev/null
+++ b/media/mtransport/test/rlogconnector_unittest.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Original author: bcampen@mozilla.com */
+
+#include "rlogconnector.h"
+
+extern "C" {
+#include "registry.h"
+#include "r_log.h"
+}
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+#include <deque>
+#include <string>
+#include <vector>
+
+using mozilla::RLogConnector;
+
+int NR_LOG_TEST = 0;
+
+class RLogConnectorTest : public ::testing::Test {
+ public:
+ RLogConnectorTest() {
+ Init();
+ }
+
+ ~RLogConnectorTest() {
+ Free();
+ }
+
+ static void SetUpTestCase() {
+ NR_reg_init(NR_REG_MODE_LOCAL);
+ r_log_init();
+ /* Would be nice to be able to unregister in the fixture */
+ const char* facility = "rlogconnector_test";
+ r_log_register(const_cast<char*>(facility), &NR_LOG_TEST);
+ }
+
+ void Init() {
+ RLogConnector::CreateInstance();
+ }
+
+ void Free() {
+ RLogConnector::DestroyInstance();
+ }
+
+ void ReInit() {
+ Free();
+ Init();
+ }
+};
+
+TEST_F(RLogConnectorTest, TestGetFree) {
+ RLogConnector* instance = RLogConnector::GetInstance();
+ ASSERT_NE(nullptr, instance);
+}
+
+TEST_F(RLogConnectorTest, TestFilterEmpty) {
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ(0U, logs.size());
+}
+
+TEST_F(RLogConnectorTest, TestBasicFilter) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->Filter("Test", 0, &logs);
+ ASSERT_EQ(1U, logs.size());
+}
+
+TEST_F(RLogConnectorTest, TestBasicFilterContent) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->Filter("Test", 0, &logs);
+ ASSERT_EQ("Test", logs.back());
+}
+
+TEST_F(RLogConnectorTest, TestFilterAnyFrontMatch) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test");
+ std::vector<std::string> substrings;
+ substrings.push_back("foo");
+ substrings.push_back("Test");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs);
+ ASSERT_EQ("Test", logs.back());
+}
+
+TEST_F(RLogConnectorTest, TestFilterAnyBackMatch) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test");
+ std::vector<std::string> substrings;
+ substrings.push_back("Test");
+ substrings.push_back("foo");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs);
+ ASSERT_EQ("Test", logs.back());
+}
+
+TEST_F(RLogConnectorTest, TestFilterAnyBothMatch) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test");
+ std::vector<std::string> substrings;
+ substrings.push_back("Tes");
+ substrings.push_back("est");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs);
+ ASSERT_EQ("Test", logs.back());
+}
+
+TEST_F(RLogConnectorTest, TestFilterAnyNeitherMatch) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test");
+ std::vector<std::string> substrings;
+ substrings.push_back("tes");
+ substrings.push_back("esT");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->FilterAny(substrings, 0, &logs);
+ ASSERT_EQ(0U, logs.size());
+}
+
+TEST_F(RLogConnectorTest, TestAllMatch) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ(2U, logs.size());
+}
+
+TEST_F(RLogConnectorTest, TestOrder) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ("Test2", logs.back());
+ ASSERT_EQ("Test1", logs.front());
+}
+
+TEST_F(RLogConnectorTest, TestNoMatch) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->Filter("foo", 0, &logs);
+ ASSERT_EQ(0U, logs.size());
+}
+
+TEST_F(RLogConnectorTest, TestSubstringFilter) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->Filter("t1", 0, &logs);
+ ASSERT_EQ(1U, logs.size());
+ ASSERT_EQ("Test1", logs.back());
+}
+
+TEST_F(RLogConnectorTest, TestFilterLimit) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test3");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test4");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test5");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test6");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->Filter("Test", 2, &logs);
+ ASSERT_EQ(2U, logs.size());
+ ASSERT_EQ("Test6", logs.back());
+ ASSERT_EQ("Test5", logs.front());
+}
+
+TEST_F(RLogConnectorTest, TestFilterAnyLimit) {
+ r_log(NR_LOG_TEST, LOG_INFO, "TestOne");
+ r_log(NR_LOG_TEST, LOG_INFO, "TestTwo");
+ r_log(NR_LOG_TEST, LOG_INFO, "TestThree");
+ r_log(NR_LOG_TEST, LOG_INFO, "TestFour");
+ r_log(NR_LOG_TEST, LOG_INFO, "TestFive");
+ r_log(NR_LOG_TEST, LOG_INFO, "TestSix");
+ std::vector<std::string> substrings;
+ // Matches Two, Three, Four, and Six
+ substrings.push_back("tT");
+ substrings.push_back("o");
+ substrings.push_back("r");
+ substrings.push_back("S");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->FilterAny(substrings, 2, &logs);
+ ASSERT_EQ(2U, logs.size());
+ ASSERT_EQ("TestSix", logs.back());
+ ASSERT_EQ("TestFour", logs.front());
+}
+
+TEST_F(RLogConnectorTest, TestLimit) {
+ RLogConnector::GetInstance()->SetLogLimit(3);
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test3");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test4");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test5");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test6");
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ(3U, logs.size());
+ ASSERT_EQ("Test6", logs.back());
+ ASSERT_EQ("Test4", logs.front());
+}
+
+TEST_F(RLogConnectorTest, TestLimitBulkDiscard) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test3");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test4");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test5");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test6");
+ RLogConnector::GetInstance()->SetLogLimit(3);
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ(3U, logs.size());
+ ASSERT_EQ("Test6", logs.back());
+ ASSERT_EQ("Test4", logs.front());
+}
+
+TEST_F(RLogConnectorTest, TestIncreaseLimit) {
+ RLogConnector::GetInstance()->SetLogLimit(3);
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test3");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test4");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test5");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test6");
+ RLogConnector::GetInstance()->SetLogLimit(300);
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ(3U, logs.size());
+ ASSERT_EQ("Test6", logs.back());
+ ASSERT_EQ("Test4", logs.front());
+}
+
+TEST_F(RLogConnectorTest, TestClear) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test3");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test4");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test5");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test6");
+ RLogConnector::GetInstance()->SetLogLimit(0);
+ RLogConnector::GetInstance()->SetLogLimit(4096);
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ(0U, logs.size());
+}
+
+TEST_F(RLogConnectorTest, TestReInit) {
+ r_log(NR_LOG_TEST, LOG_INFO, "Test1");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test2");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test3");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test4");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test5");
+ r_log(NR_LOG_TEST, LOG_INFO, "Test6");
+ ReInit();
+ std::deque<std::string> logs;
+ RLogConnector::GetInstance()->GetAny(0, &logs);
+ ASSERT_EQ(0U, logs.size());
+}
diff --git a/media/mtransport/test/runnable_utils_unittest.cpp b/media/mtransport/test/runnable_utils_unittest.cpp
new file mode 100644
index 000000000..28ca57aa1
--- /dev/null
+++ b/media/mtransport/test/runnable_utils_unittest.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+#include <iostream>
+
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMGlue.h"
+
+#include "mozilla/RefPtr.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransportService.h"
+
+#include "nsASocketHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+
+#include "runnable_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+namespace {
+
+class Destructor {
+ private:
+ ~Destructor() {
+ std::cerr << "Destructor called" << std::endl;
+ *destroyed_ = true;
+ }
+ public:
+ explicit Destructor(bool* destroyed) : destroyed_(destroyed) {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Destructor)
+
+ private:
+ bool *destroyed_;
+};
+
+class TargetClass {
+ public:
+ explicit TargetClass(int *ran) : ran_(ran) {}
+
+ void m1(int x) {
+ std::cerr << __FUNCTION__ << " " << x << std::endl;
+ *ran_ = 1;
+ }
+
+ void m2(int x, int y) {
+ std::cerr << __FUNCTION__ << " " << x << " " << y << std::endl;
+ *ran_ = 2;
+ }
+
+ void m1set(bool *z) {
+ std::cerr << __FUNCTION__ << std::endl;
+ *z = true;
+ }
+ int return_int(int x) {
+ std::cerr << __FUNCTION__ << std::endl;
+ return x;
+ }
+ void destructor_target(Destructor*) {
+ }
+
+ void destructor_target_ref(RefPtr<Destructor> destructor) {
+ }
+
+ int *ran_;
+};
+
+
+class RunnableArgsTest : public MtransportTest {
+ public:
+ RunnableArgsTest() : MtransportTest(), ran_(0), cl_(&ran_){}
+
+ void Test1Arg() {
+ Runnable * r = WrapRunnable(&cl_, &TargetClass::m1, 1);
+ r->Run();
+ ASSERT_EQ(1, ran_);
+ }
+
+ void Test2Args() {
+ Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2);
+ r->Run();
+ ASSERT_EQ(2, ran_);
+ }
+
+ private:
+ int ran_;
+ TargetClass cl_;
+};
+
+class DispatchTest : public MtransportTest {
+ public:
+ DispatchTest() : MtransportTest(), ran_(0), cl_(&ran_) {}
+
+ void SetUp() {
+ MtransportTest::SetUp();
+
+ nsresult rv;
+ target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ }
+
+ void Test1Arg() {
+ Runnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1);
+ target_->Dispatch(r, NS_DISPATCH_SYNC);
+ ASSERT_EQ(1, ran_);
+ }
+
+ void Test2Args() {
+ Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2);
+ target_->Dispatch(r, NS_DISPATCH_SYNC);
+ ASSERT_EQ(2, ran_);
+ }
+
+ void Test1Set() {
+ bool x = false;
+ target_->Dispatch(WrapRunnable(&cl_, &TargetClass::m1set, &x),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(x);
+ }
+
+ void TestRet() {
+ int z;
+ int x = 10;
+
+ target_->Dispatch(WrapRunnableRet(&z, &cl_, &TargetClass::return_int, x),
+ NS_DISPATCH_SYNC);
+ ASSERT_EQ(10, z);
+ }
+
+ protected:
+ int ran_;
+ TargetClass cl_;
+ nsCOMPtr<nsIEventTarget> target_;
+};
+
+
+TEST_F(RunnableArgsTest, OneArgument) {
+ Test1Arg();
+}
+
+TEST_F(RunnableArgsTest, TwoArguments) {
+ Test2Args();
+}
+
+TEST_F(DispatchTest, OneArgument) {
+ Test1Arg();
+}
+
+TEST_F(DispatchTest, TwoArguments) {
+ Test2Args();
+}
+
+TEST_F(DispatchTest, Test1Set) {
+ Test1Set();
+}
+
+TEST_F(DispatchTest, TestRet) {
+ TestRet();
+}
+
+void SetNonMethod(TargetClass *cl, int x) {
+ cl->m1(x);
+}
+
+int SetNonMethodRet(TargetClass *cl, int x) {
+ cl->m1(x);
+
+ return x;
+}
+
+TEST_F(DispatchTest, TestNonMethod) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNM(SetNonMethod, &cl_, 10), NS_DISPATCH_SYNC);
+
+ ASSERT_EQ(1, ran_);
+}
+
+TEST_F(DispatchTest, TestNonMethodRet) {
+ int z;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNMRet(&z, SetNonMethodRet, &cl_, 10), NS_DISPATCH_SYNC);
+
+ ASSERT_EQ(1, ran_);
+ ASSERT_EQ(10, z);
+}
+
+TEST_F(DispatchTest, TestDestructor) {
+ bool destroyed = false;
+ RefPtr<Destructor> destructor = new Destructor(&destroyed);
+ target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target,
+ destructor),
+ NS_DISPATCH_SYNC);
+ ASSERT_FALSE(destroyed);
+ destructor = nullptr;
+ ASSERT_TRUE(destroyed);
+}
+
+TEST_F(DispatchTest, TestDestructorRef) {
+ bool destroyed = false;
+ RefPtr<Destructor> destructor = new Destructor(&destroyed);
+ target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target_ref,
+ destructor),
+ NS_DISPATCH_SYNC);
+ ASSERT_FALSE(destroyed);
+ destructor = nullptr;
+ ASSERT_TRUE(destroyed);
+}
+
+
+} // end of namespace
diff --git a/media/mtransport/test/sctp_unittest.cpp b/media/mtransport/test/sctp_unittest.cpp
new file mode 100644
index 000000000..886589cae
--- /dev/null
+++ b/media/mtransport/test/sctp_unittest.cpp
@@ -0,0 +1,393 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include <iostream>
+#include <string>
+#include <map>
+
+#include "sigslot.h"
+
+#include "logging.h"
+#include "nsNetCID.h"
+#include "nsITimer.h"
+#include "nsComponentManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+#include "transportflow.h"
+#include "transportlayer.h"
+#include "transportlayerloopback.h"
+
+#include "runnable_utils.h"
+#include "usrsctp.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+
+using namespace mozilla;
+
+static bool sctp_logging = false;
+static int port_number = 5000;
+
+namespace {
+
+class TransportTestPeer;
+
+class SendPeriodic : public nsITimerCallback {
+ public:
+ SendPeriodic(TransportTestPeer *peer, int to_send) :
+ peer_(peer),
+ to_send_(to_send) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+ protected:
+ virtual ~SendPeriodic() {}
+
+ TransportTestPeer *peer_;
+ int to_send_;
+};
+
+NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback)
+
+
+class TransportTestPeer : public sigslot::has_slots<> {
+ public:
+ TransportTestPeer(std::string name, int local_port, int remote_port,
+ MtransportTestUtils* utils)
+ : name_(name), connected_(false),
+ sent_(0), received_(0),
+ flow_(new TransportFlow()),
+ loopback_(new TransportLayerLoopback()),
+ sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)),
+ timer_(do_CreateInstance(NS_TIMER_CONTRACTID)),
+ periodic_(nullptr),
+ test_utils_(utils) {
+ std::cerr << "Creating TransportTestPeer; flow=" <<
+ static_cast<void *>(flow_.get()) <<
+ " local=" << local_port <<
+ " remote=" << remote_port << std::endl;
+
+ usrsctp_register_address(static_cast<void *>(this));
+ int r = usrsctp_set_non_blocking(sctp_, 1);
+ EXPECT_GE(r, 0);
+
+ struct linger l;
+ l.l_onoff = 1;
+ l.l_linger = 0;
+ r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l,
+ (socklen_t)sizeof(l));
+ EXPECT_GE(r, 0);
+
+ struct sctp_event subscription;
+ memset(&subscription, 0, sizeof(subscription));
+ subscription.se_assoc_id = SCTP_ALL_ASSOC;
+ subscription.se_on = 1;
+ subscription.se_type = SCTP_ASSOC_CHANGE;
+ r = usrsctp_setsockopt(sctp_, IPPROTO_SCTP, SCTP_EVENT, &subscription,
+ sizeof(subscription));
+ EXPECT_GE(r, 0);
+
+ memset(&local_addr_, 0, sizeof(local_addr_));
+ local_addr_.sconn_family = AF_CONN;
+#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
+ local_addr_.sconn_len = sizeof(struct sockaddr_conn);
+#endif
+ local_addr_.sconn_port = htons(local_port);
+ local_addr_.sconn_addr = static_cast<void *>(this);
+
+
+ memset(&remote_addr_, 0, sizeof(remote_addr_));
+ remote_addr_.sconn_family = AF_CONN;
+#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
+ remote_addr_.sconn_len = sizeof(struct sockaddr_conn);
+#endif
+ remote_addr_.sconn_port = htons(remote_port);
+ remote_addr_.sconn_addr = static_cast<void *>(this);
+
+ nsresult res;
+ res = loopback_->Init();
+ EXPECT_EQ((nsresult)NS_OK, res);
+ }
+
+ ~TransportTestPeer() {
+ std::cerr << "Destroying sctp connection flow=" <<
+ static_cast<void *>(flow_.get()) << std::endl;
+ usrsctp_close(sctp_);
+ usrsctp_deregister_address(static_cast<void *>(this));
+
+ test_utils_->sts_target()->Dispatch(WrapRunnable(this,
+ &TransportTestPeer::Disconnect_s),
+ NS_DISPATCH_SYNC);
+
+ std::cerr << "~TransportTestPeer() completed" << std::endl;
+ }
+
+ void ConnectSocket(TransportTestPeer *peer) {
+ test_utils_->sts_target()->Dispatch(WrapRunnable(
+ this, &TransportTestPeer::ConnectSocket_s, peer),
+ NS_DISPATCH_SYNC);
+ }
+
+ void ConnectSocket_s(TransportTestPeer *peer) {
+ loopback_->Connect(peer->loopback_);
+
+ ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
+
+ flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+
+ // SCTP here!
+ ASSERT_TRUE(sctp_);
+ std::cerr << "Calling usrsctp_bind()" << std::endl;
+ int r = usrsctp_bind(sctp_, reinterpret_cast<struct sockaddr *>(
+ &local_addr_), sizeof(local_addr_));
+ ASSERT_GE(0, r);
+
+ std::cerr << "Calling usrsctp_connect()" << std::endl;
+ r = usrsctp_connect(sctp_, reinterpret_cast<struct sockaddr *>(
+ &remote_addr_), sizeof(remote_addr_));
+ ASSERT_GE(0, r);
+ }
+
+ void Disconnect_s() {
+ if (flow_) {
+ flow_ = nullptr;
+ }
+ }
+
+ void Disconnect() {
+ loopback_->Disconnect();
+ }
+
+
+ void StartTransfer(size_t to_send) {
+ periodic_ = new SendPeriodic(this, to_send);
+ timer_->SetTarget(test_utils_->sts_target());
+ timer_->InitWithCallback(periodic_, 10, nsITimer::TYPE_REPEATING_SLACK);
+ }
+
+ void SendOne() {
+ unsigned char buf[100];
+ memset(buf, sent_ & 0xff, sizeof(buf));
+
+ struct sctp_sndinfo info;
+ info.snd_sid = 1;
+ info.snd_flags = 0;
+ info.snd_ppid = 50; // What the heck is this?
+ info.snd_context = 0;
+ info.snd_assoc_id = 0;
+
+ int r = usrsctp_sendv(sctp_, buf, sizeof(buf), nullptr, 0,
+ static_cast<void *>(&info),
+ sizeof(info), SCTP_SENDV_SNDINFO, 0);
+ ASSERT_TRUE(r >= 0);
+ ASSERT_EQ(sizeof(buf), (size_t)r);
+
+ ++sent_;
+ }
+
+ int sent() const { return sent_; }
+ int received() const { return received_; }
+ bool connected() const { return connected_; }
+
+ static TransportResult SendPacket_s(const unsigned char* data, size_t len,
+ const RefPtr<TransportFlow>& flow) {
+ TransportResult res = flow->SendPacket(data, len);
+ delete data; // we always allocate
+ return res;
+ }
+
+ TransportResult SendPacket(const unsigned char* data, size_t len) {
+ unsigned char *buffer = new unsigned char[len];
+ memcpy(buffer, data, len);
+
+ // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called
+ // from MainThread especially during shutdown (same as DataChannels).
+ // RUN_ON_THREAD short-circuits if already on the STS thread, which is
+ // normal for most transfers outside of connect() and close(). Passes
+ // a refptr to flow_ to avoid any async deletion issues (since we can't
+ // make 'this' into a refptr as it isn't refcounted)
+ RUN_ON_THREAD(test_utils_->sts_target(), WrapRunnableNM(
+ &TransportTestPeer::SendPacket_s, buffer, len, flow_),
+ NS_DISPATCH_NORMAL);
+
+ return 0;
+ }
+
+ void PacketReceived(TransportFlow * flow, const unsigned char* data,
+ size_t len) {
+ std::cerr << "Received " << len << " bytes" << std::endl;
+
+ // Pass the data to SCTP
+
+ usrsctp_conninput(static_cast<void *>(this), data, len, 0);
+ }
+
+ // Process SCTP notification
+ void Notification(union sctp_notification *msg, size_t len) {
+ ASSERT_EQ(msg->sn_header.sn_length, len);
+
+ if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
+ struct sctp_assoc_change *change = &msg->sn_assoc_change;
+
+ if (change->sac_state == SCTP_COMM_UP) {
+ std::cerr << "Connection up" << std::endl;
+ SetConnected(true);
+ } else {
+ std::cerr << "Connection down" << std::endl;
+ SetConnected(false);
+ }
+ }
+ }
+
+ void SetConnected(bool state) {
+ connected_ = state;
+ }
+
+ static int conn_output(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df) {
+ TransportTestPeer *peer = static_cast<TransportTestPeer *>(addr);
+
+ peer->SendPacket(static_cast<unsigned char *>(buffer), length);
+
+ return 0;
+ }
+
+ static int receive_cb(struct socket* sock, union sctp_sockstore addr,
+ void *data, size_t datalen,
+ struct sctp_rcvinfo rcv, int flags, void *ulp_info) {
+ TransportTestPeer *me = static_cast<TransportTestPeer *>(
+ addr.sconn.sconn_addr);
+ MOZ_ASSERT(me);
+
+ if (flags & MSG_NOTIFICATION) {
+ union sctp_notification *notif =
+ static_cast<union sctp_notification *>(data);
+
+ me->Notification(notif, datalen);
+ return 0;
+ }
+
+ me->received_ += datalen;
+
+ std::cerr << "receive_cb: sock " << sock << " data " << data << "(" << datalen << ") total received bytes = " << me->received_ << std::endl;
+
+ return 0;
+ }
+
+
+ private:
+ std::string name_;
+ bool connected_;
+ size_t sent_;
+ size_t received_;
+ RefPtr<TransportFlow> flow_;
+ TransportLayerLoopback *loopback_;
+
+ struct sockaddr_conn local_addr_;
+ struct sockaddr_conn remote_addr_;
+ struct socket *sctp_;
+ nsCOMPtr<nsITimer> timer_;
+ RefPtr<SendPeriodic> periodic_;
+ MtransportTestUtils* test_utils_;
+};
+
+
+// Implemented here because it calls a method of TransportTestPeer
+NS_IMETHODIMP SendPeriodic::Notify(nsITimer *timer) {
+ peer_->SendOne();
+ --to_send_;
+ if (!to_send_) {
+ timer->Cancel();
+ }
+ return NS_OK;
+}
+
+class SctpTransportTest : public MtransportTest {
+ public:
+ SctpTransportTest() {
+ }
+
+ ~SctpTransportTest() {
+ }
+
+ static void debug_printf(const char *format, ...) {
+ va_list ap;
+
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ }
+
+
+ static void SetUpTestCase() {
+ if (sctp_logging) {
+ usrsctp_init(0, &TransportTestPeer::conn_output, debug_printf);
+ usrsctp_sysctl_set_sctp_debug_on(0xffffffff);
+ } else {
+ usrsctp_init(0, &TransportTestPeer::conn_output, nullptr);
+ }
+ }
+
+ void TearDown() override {
+ if (p1_)
+ p1_->Disconnect();
+ if (p2_)
+ p2_->Disconnect();
+ delete p1_;
+ delete p2_;
+
+ MtransportTest::TearDown();
+ }
+
+
+ void ConnectSocket(int p1port = 0, int p2port = 0) {
+ if (!p1port)
+ p1port = port_number++;
+ if (!p2port)
+ p2port = port_number++;
+
+ p1_ = new TransportTestPeer("P1", p1port, p2port, test_utils_);
+ p2_ = new TransportTestPeer("P2", p2port, p1port, test_utils_);
+
+ p1_->ConnectSocket(p2_);
+ p2_->ConnectSocket(p1_);
+ ASSERT_TRUE_WAIT(p1_->connected(), 2000);
+ ASSERT_TRUE_WAIT(p2_->connected(), 2000);
+ }
+
+ void TestTransfer(int expected = 1) {
+ std::cerr << "Starting trasnsfer test" << std::endl;
+ p1_->StartTransfer(expected);
+ ASSERT_TRUE_WAIT(p1_->sent() == expected, 10000);
+ ASSERT_TRUE_WAIT(p2_->received() == (expected * 100), 10000);
+ std::cerr << "P2 received " << p2_->received() << std::endl;
+ }
+
+ protected:
+ TransportTestPeer *p1_;
+ TransportTestPeer *p2_;
+};
+
+TEST_F(SctpTransportTest, TestConnect) {
+ ConnectSocket();
+}
+
+TEST_F(SctpTransportTest, TestConnectSymmetricalPorts) {
+ ConnectSocket(5002,5002);
+}
+
+TEST_F(SctpTransportTest, TestTransfer) {
+ ConnectSocket();
+ TestTransfer(50);
+}
+
+
+} // end namespace
diff --git a/media/mtransport/test/simpletokenbucket_unittest.cpp b/media/mtransport/test/simpletokenbucket_unittest.cpp
new file mode 100644
index 000000000..70c962bb8
--- /dev/null
+++ b/media/mtransport/test/simpletokenbucket_unittest.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Original author: bcampen@mozilla.com */
+
+#include "simpletokenbucket.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using mozilla::SimpleTokenBucket;
+
+class TestSimpleTokenBucket : public SimpleTokenBucket {
+ public:
+ TestSimpleTokenBucket(size_t bucketSize, size_t tokensPerSecond) :
+ SimpleTokenBucket(bucketSize, tokensPerSecond) {
+ }
+
+ void fastForward(int32_t timeMilliSeconds) {
+ if (timeMilliSeconds >= 0) {
+ last_time_tokens_added_ -= PR_MillisecondsToInterval(timeMilliSeconds);
+ } else {
+ last_time_tokens_added_ += PR_MillisecondsToInterval(-timeMilliSeconds);
+ }
+ }
+};
+
+TEST(SimpleTokenBucketTest, TestConstruct) {
+ TestSimpleTokenBucket b(10, 1);
+}
+
+TEST(SimpleTokenBucketTest, TestGet) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(5U, b.getTokens(5));
+}
+
+TEST(SimpleTokenBucketTest, TestGetAll) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(10U, b.getTokens(10));
+}
+
+TEST(SimpleTokenBucketTest, TestGetInsufficient) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(5U, b.getTokens(5));
+ ASSERT_EQ(5U, b.getTokens(6));
+}
+
+TEST(SimpleTokenBucketTest, TestGetBucketCount) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(10U, b.getTokens(UINT32_MAX));
+ ASSERT_EQ(5U, b.getTokens(5));
+ ASSERT_EQ(5U, b.getTokens(UINT32_MAX));
+}
+
+TEST(SimpleTokenBucketTest, TestTokenRefill) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(5U, b.getTokens(5));
+ b.fastForward(1000);
+ ASSERT_EQ(6U, b.getTokens(6));
+}
+
+TEST(SimpleTokenBucketTest, TestNoTimeWasted) {
+ // Makes sure that when the time elapsed is insufficient to add any
+ // tokens to the bucket, the internal timestamp that is used in this
+ // calculation is not updated (ie; two subsequent 0.5 second elapsed times
+ // counts as a full second)
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(5U, b.getTokens(5));
+ b.fastForward(500);
+ ASSERT_EQ(5U, b.getTokens(6));
+ b.fastForward(500);
+ ASSERT_EQ(6U, b.getTokens(6));
+}
+
+TEST(SimpleTokenBucketTest, TestNegativeTime) {
+ TestSimpleTokenBucket b(10, 1);
+ b.fastForward(-1000);
+ // Make sure we don't end up with an invalid number of tokens, but otherwise
+ // permit anything.
+ ASSERT_GT(11U, b.getTokens(100));
+}
+
+TEST(SimpleTokenBucketTest, TestEmptyBucket) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(10U, b.getTokens(10));
+ ASSERT_EQ(0U, b.getTokens(10));
+}
+
+TEST(SimpleTokenBucketTest, TestEmptyThenFillBucket) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(10U, b.getTokens(10));
+ ASSERT_EQ(0U, b.getTokens(1));
+ b.fastForward(50000);
+ ASSERT_EQ(10U, b.getTokens(10));
+}
+
+TEST(SimpleTokenBucketTest, TestNoOverflow) {
+ TestSimpleTokenBucket b(10, 1);
+ ASSERT_EQ(10U, b.getTokens(10));
+ ASSERT_EQ(0U, b.getTokens(1));
+ b.fastForward(50000);
+ ASSERT_EQ(10U, b.getTokens(11));
+}
diff --git a/media/mtransport/test/sockettransportservice_unittest.cpp b/media/mtransport/test/sockettransportservice_unittest.cpp
new file mode 100644
index 000000000..43746c908
--- /dev/null
+++ b/media/mtransport/test/sockettransportservice_unittest.cpp
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+#include <iostream>
+
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMGlue.h"
+
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransportService.h"
+
+#include "nsASocketHandler.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+namespace {
+class SocketTransportServiceTest : public MtransportTest {
+ public:
+ SocketTransportServiceTest() : MtransportTest(),
+ received_(0),
+ readpipe_(nullptr),
+ writepipe_(nullptr),
+ registered_(false) {
+ }
+
+ ~SocketTransportServiceTest() {
+ if (readpipe_)
+ PR_Close(readpipe_);
+ if (writepipe_)
+ PR_Close(writepipe_);
+ }
+
+ void SetUp();
+ void RegisterHandler();
+ void SendEvent();
+ void SendPacket();
+
+ void ReceivePacket() {
+ ++received_;
+ }
+
+ void ReceiveEvent() {
+ ++received_;
+ }
+
+ size_t Received() {
+ return received_;
+ }
+
+ private:
+ nsCOMPtr<nsISocketTransportService> stservice_;
+ nsCOMPtr<nsIEventTarget> target_;
+ size_t received_;
+ PRFileDesc *readpipe_;
+ PRFileDesc *writepipe_;
+ bool registered_;
+};
+
+
+// Received an event.
+class EventReceived : public Runnable {
+public:
+ explicit EventReceived(SocketTransportServiceTest *test) :
+ test_(test) {}
+
+ NS_IMETHOD Run() override {
+ test_->ReceiveEvent();
+ return NS_OK;
+ }
+
+ SocketTransportServiceTest *test_;
+};
+
+
+// Register our listener on the socket
+class RegisterEvent : public Runnable {
+public:
+ explicit RegisterEvent(SocketTransportServiceTest *test) :
+ test_(test) {}
+
+ NS_IMETHOD Run() override {
+ test_->RegisterHandler();
+ return NS_OK;
+ }
+
+ SocketTransportServiceTest *test_;
+};
+
+
+class SocketHandler : public nsASocketHandler {
+ public:
+ explicit SocketHandler(SocketTransportServiceTest *test) : test_(test) {
+ }
+
+ void OnSocketReady(PRFileDesc *fd, int16_t outflags) override {
+ unsigned char buf[1600];
+
+ int32_t rv;
+ rv = PR_Recv(fd, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT);
+ if (rv > 0) {
+ std::cerr << "Read " << rv << " bytes" << std::endl;
+ test_->ReceivePacket();
+ }
+ }
+
+ void OnSocketDetached(PRFileDesc *fd) override {}
+
+ void IsLocal(bool *aIsLocal) override {
+ // TODO(jesup): better check? Does it matter? (likely no)
+ *aIsLocal = false;
+ }
+
+ virtual uint64_t ByteCountSent() override { return 0; }
+ virtual uint64_t ByteCountReceived() override { return 0; }
+
+ NS_DECL_ISUPPORTS
+
+ protected:
+ virtual ~SocketHandler() {}
+
+ private:
+ SocketTransportServiceTest *test_;
+};
+
+NS_IMPL_ISUPPORTS0(SocketHandler)
+
+void SocketTransportServiceTest::SetUp() {
+ MtransportTest::SetUp();
+
+ // Get the transport service as a dispatch target
+ nsresult rv;
+ target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Get the transport service as a transport service
+ stservice_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // Create a loopback pipe
+ PRStatus status = PR_CreatePipe(&readpipe_, &writepipe_);
+ ASSERT_EQ(status, PR_SUCCESS);
+
+ // Register ourselves as a listener for the read side of the
+ // socket. The registration has to happen on the STS thread,
+ // hence this event stuff.
+ rv = target_->Dispatch(new RegisterEvent(this), 0);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE_WAIT(registered_, 10000);
+
+}
+
+void SocketTransportServiceTest::RegisterHandler() {
+ nsresult rv;
+
+ rv = stservice_->AttachSocket(readpipe_, new SocketHandler(this));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ registered_ = true;
+}
+
+void SocketTransportServiceTest::SendEvent() {
+ nsresult rv;
+
+ rv = target_->Dispatch(new EventReceived(this), 0);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+ ASSERT_TRUE_WAIT(Received() == 1, 10000);
+}
+
+void SocketTransportServiceTest::SendPacket() {
+ unsigned char buffer[1024];
+ memset(buffer, 0, sizeof(buffer));
+
+ int32_t status = PR_Write(writepipe_, buffer, sizeof(buffer));
+ uint32_t size = status & 0xffff;
+ ASSERT_EQ(sizeof(buffer), size);
+}
+
+
+
+// The unit tests themselves
+TEST_F(SocketTransportServiceTest, SendEvent) {
+ SendEvent();
+}
+
+TEST_F(SocketTransportServiceTest, SendPacket) {
+ SendPacket();
+}
+
+
+} // end namespace
diff --git a/media/mtransport/test/stunserver.cpp b/media/mtransport/test/stunserver.cpp
new file mode 100644
index 000000000..7318c7c95
--- /dev/null
+++ b/media/mtransport/test/stunserver.cpp
@@ -0,0 +1,664 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+/*
+Original code from nICEr and nrappkit.
+
+nICEr copyright:
+
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+nrappkit copyright:
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Thu Dec 20 20:14:49 2001
+*/
+#include "logging.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "databuffer.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "async_wait.h"
+#include "async_timer.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "transport_addr.h"
+#include "addrs.h"
+#include "local_addr.h"
+#include "stun_util.h"
+#include "registry.h"
+#include "nr_socket_buffered_stun.h"
+}
+
+#include "stunserver.h"
+
+#include <string>
+
+MOZ_MTLOG_MODULE("stunserver");
+
+namespace mozilla {
+
+// Wrapper nr_socket which allows us to lie to the stun server about the
+// IP address.
+struct nr_socket_wrapped {
+ nr_socket *sock_;
+ nr_transport_addr addr_;
+};
+
+static int nr_socket_wrapped_destroy(void **objp) {
+ if (!objp || !*objp)
+ return 0;
+
+ nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(*objp);
+ *objp = 0;
+
+ delete wrapped;
+
+ return 0;
+}
+
+static int nr_socket_wrapped_sendto(void *obj, const void *msg, size_t len, int flags,
+ nr_transport_addr *addr) {
+ nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj);
+
+ return nr_socket_sendto(wrapped->sock_, msg, len, flags, &wrapped->addr_);
+}
+
+static int nr_socket_wrapped_recvfrom(void *obj, void * restrict buf, size_t maxlen,
+ size_t *len, int flags, nr_transport_addr *addr) {
+ nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj);
+
+ return nr_socket_recvfrom(wrapped->sock_, buf, maxlen, len, flags, addr);
+}
+
+static int nr_socket_wrapped_getfd(void *obj, NR_SOCKET *fd) {
+ nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj);
+
+ return nr_socket_getfd(wrapped->sock_, fd);
+}
+
+static int nr_socket_wrapped_getaddr(void *obj, nr_transport_addr *addrp) {
+ nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj);
+
+ return nr_socket_getaddr(wrapped->sock_, addrp);
+}
+
+static int nr_socket_wrapped_close(void *obj) {
+ MOZ_CRASH();
+}
+
+static int nr_socket_wrapped_set_send_addr(nr_socket *sock, nr_transport_addr *addr) {
+ nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(sock->obj);
+
+ return nr_transport_addr_copy(&wrapped->addr_, addr);
+}
+
+static nr_socket_vtbl nr_socket_wrapped_vtbl = {
+ 2,
+ nr_socket_wrapped_destroy,
+ nr_socket_wrapped_sendto,
+ nr_socket_wrapped_recvfrom,
+ nr_socket_wrapped_getfd,
+ nr_socket_wrapped_getaddr,
+ 0,
+ 0,
+ 0,
+ nr_socket_wrapped_close,
+ 0,
+ 0
+};
+
+int nr_socket_wrapped_create(nr_socket *inner, nr_socket **outp) {
+ auto wrapped = MakeUnique<nr_socket_wrapped>();
+
+ wrapped->sock_ = inner;
+
+ int r = nr_socket_create_int(wrapped.get(), &nr_socket_wrapped_vtbl, outp);
+ if (r)
+ return r;
+
+ Unused << wrapped.release();
+ return 0;
+}
+
+
+// Instance static.
+// Note: Calling Create() at static init time is not going to be safe, since
+// we have no reason to expect this will be initted to a nullptr yet.
+TestStunServer *TestStunServer::instance;
+TestStunTcpServer *TestStunTcpServer::instance;
+TestStunServer *TestStunServer::instance6;
+TestStunTcpServer *TestStunTcpServer::instance6;
+uint16_t TestStunServer::instance_port = 3478;
+uint16_t TestStunTcpServer::instance_port = 3478;
+
+TestStunServer::~TestStunServer() {
+ // TODO(ekr@rtfm.com): Put this on the right thread.
+
+ // Unhook callback from our listen socket.
+ if (listen_sock_) {
+ NR_SOCKET fd;
+ if (!nr_socket_getfd(listen_sock_, &fd)) {
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
+ }
+ }
+
+ // Free up stun context and network resources
+ nr_stun_server_ctx_destroy(&stun_server_);
+ nr_socket_destroy(&listen_sock_);
+ nr_socket_destroy(&send_sock_);
+
+ // Make sure we aren't still waiting on a deferred response timer to pop
+ if (timer_handle_)
+ NR_async_timer_cancel(timer_handle_);
+
+ delete response_addr_;
+}
+
+int TestStunServer::SetInternalPort(nr_local_addr *addr, uint16_t port) {
+ if (nr_transport_addr_set_port(&addr->addr, port)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set port");
+ return R_INTERNAL;
+ }
+
+ if (nr_transport_addr_fmt_addr_string(&addr->addr)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't re-set addr string");
+ return R_INTERNAL;
+ }
+
+ return 0;
+}
+
+int TestStunServer::TryOpenListenSocket(nr_local_addr *addr, uint16_t port) {
+
+ int r = SetInternalPort(addr, port);
+
+ if (r)
+ return r;
+
+ if (nr_socket_local_create(nullptr, &addr->addr, &listen_sock_)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket");
+ return R_ALREADY;
+ }
+
+ return 0;
+}
+
+int TestStunServer::Initialize(int address_family) {
+ static const size_t max_addrs = 100;
+ nr_local_addr addrs[max_addrs];
+ int addr_ct;
+ int r;
+ int i;
+
+ r = nr_stun_find_local_addresses(addrs, max_addrs, &addr_ct);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't retrieve addresses");
+ return R_INTERNAL;
+ }
+
+ if (addr_ct < 1) {
+ MOZ_MTLOG(ML_ERROR, "No local addresses");
+ return R_INTERNAL;
+ }
+
+ for (i = 0; i < addr_ct; ++i) {
+ if (addrs[i].addr.addr->sa_family == address_family) {
+ break;
+ }
+ }
+
+ if (i == addr_ct) {
+ MOZ_MTLOG(ML_ERROR, "No local addresses of the configured IP version");
+ return R_INTERNAL;
+ }
+
+ int tries = 100;
+ while (tries--) {
+ // Bind on configured port (default 3478)
+ r = TryOpenListenSocket(&addrs[i], instance_port);
+ // We interpret R_ALREADY to mean the addr is probably in use. Try another.
+ // Otherwise, it either worked or it didn't, and we check below.
+ if (r != R_ALREADY) {
+ break;
+ }
+ ++instance_port;
+ }
+
+ if (r) {
+ return R_INTERNAL;
+ }
+
+ r = nr_socket_wrapped_create(listen_sock_, &send_sock_);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create send socket");
+ return R_INTERNAL;
+ }
+
+ r = nr_stun_server_ctx_create(const_cast<char *>("Test STUN server"),
+ send_sock_,
+ &stun_server_);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create STUN server");
+ return R_INTERNAL;
+ }
+
+ // Cache the address and port.
+ char addr_string[INET6_ADDRSTRLEN];
+ r = nr_transport_addr_get_addrstring(&addrs[i].addr, addr_string,
+ sizeof(addr_string));
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Failed to convert listen addr to a string representation");
+ return R_INTERNAL;
+ }
+
+ listen_addr_ = addr_string;
+ listen_port_ = instance_port;
+
+ return 0;
+}
+
+UniquePtr<TestStunServer> TestStunServer::Create(int address_family) {
+ NR_reg_init(NR_REG_MODE_LOCAL);
+
+ UniquePtr<TestStunServer> server(new TestStunServer());
+
+ if (server->Initialize(address_family))
+ return nullptr;
+
+ NR_SOCKET fd;
+ int r = nr_socket_getfd(server->listen_sock_, &fd);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get fd");
+ return nullptr;
+ }
+
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server.get());
+
+ return server;
+}
+
+void TestStunServer::ConfigurePort(uint16_t port) {
+ instance_port = port;
+}
+
+TestStunServer* TestStunServer::GetInstance(int address_family) {
+ switch (address_family) {
+ case AF_INET:
+ if (!instance)
+ instance = Create(address_family).release();
+
+ MOZ_ASSERT(instance);
+ return instance;
+ case AF_INET6:
+ if (!instance6)
+ instance6 = Create(address_family).release();
+
+ return instance6;
+ default:
+ MOZ_CRASH();
+ }
+}
+
+void TestStunServer::ShutdownInstance() {
+ delete instance;
+ instance = nullptr;
+ delete instance6;
+ instance6 = nullptr;
+}
+
+
+struct DeferredStunOperation {
+ DeferredStunOperation(TestStunServer *server,
+ const char *data, size_t len,
+ nr_transport_addr *addr,
+ nr_socket *sock) :
+ server_(server),
+ buffer_(reinterpret_cast<const uint8_t *>(data), len),
+ sock_(sock) {
+ nr_transport_addr_copy(&addr_, addr);
+ }
+
+ TestStunServer *server_;
+ DataBuffer buffer_;
+ nr_transport_addr addr_;
+ nr_socket *sock_;
+};
+
+void TestStunServer::Process(const uint8_t *msg, size_t len, nr_transport_addr *addr, nr_socket *sock) {
+
+ if (!sock) {
+ sock = send_sock_;
+ }
+
+ // Set the wrapped address so that the response goes to the right place.
+ nr_socket_wrapped_set_send_addr(sock, addr);
+
+ nr_stun_server_process_request(stun_server_, sock,
+ const_cast<char *>(reinterpret_cast<const char *>(msg)),
+ len,
+ response_addr_ ?
+ response_addr_ : addr,
+ NR_STUN_AUTH_RULE_OPTIONAL);
+}
+
+void TestStunServer::process_cb(NR_SOCKET s, int how, void *cb_arg) {
+ DeferredStunOperation *op = static_cast<DeferredStunOperation *>(cb_arg);
+ op->server_->timer_handle_ = nullptr;
+ op->server_->Process(op->buffer_.data(), op->buffer_.len(), &op->addr_, op->sock_);
+
+ delete op;
+}
+
+nr_socket* TestStunServer::GetReceivingSocket(NR_SOCKET s) {
+ return listen_sock_;
+}
+
+nr_socket* TestStunServer::GetSendingSocket(nr_socket *sock) {
+ return send_sock_;
+}
+
+void TestStunServer::readable_cb(NR_SOCKET s, int how, void *cb_arg) {
+ TestStunServer *server = static_cast<TestStunServer*>(cb_arg);
+
+ char message[max_stun_message_size];
+ size_t message_len;
+ nr_transport_addr addr;
+ nr_socket *recv_sock = server->GetReceivingSocket(s);
+ if (!recv_sock) {
+ MOZ_MTLOG(ML_ERROR, "Failed to lookup receiving socket");
+ return;
+ }
+ nr_socket *send_sock = server->GetSendingSocket(recv_sock);
+
+ /* Re-arm. */
+ NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server);
+
+ if (nr_socket_recvfrom(recv_sock, message, sizeof(message),
+ &message_len, 0, &addr)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't read STUN message");
+ return;
+ }
+
+ MOZ_MTLOG(ML_DEBUG, "Received data of length " << message_len);
+
+ // If we have initial dropping set, check at this point.
+ std::string key(addr.as_string);
+
+ if (server->received_ct_.count(key) == 0) {
+ server->received_ct_[key] = 0;
+ }
+
+ ++server->received_ct_[key];
+
+ if (!server->active_ || (server->received_ct_[key] <= server->initial_ct_)) {
+ MOZ_MTLOG(ML_DEBUG, "Dropping message #"
+ << server->received_ct_[key] << " from " << key);
+ return;
+ }
+
+ if (server->delay_ms_) {
+ NR_ASYNC_TIMER_SET(server->delay_ms_,
+ process_cb,
+ new DeferredStunOperation(
+ server,
+ message, message_len,
+ &addr, send_sock),
+ &server->timer_handle_);
+ } else {
+ server->Process(reinterpret_cast<const uint8_t *>(message), message_len,
+ &addr, send_sock);
+ }
+}
+
+void TestStunServer::SetActive(bool active) {
+ active_ = active;
+}
+
+void TestStunServer::SetDelay(uint32_t delay_ms) {
+ delay_ms_ = delay_ms;
+}
+
+void TestStunServer::SetDropInitialPackets(uint32_t count) {
+ initial_ct_ = count;
+}
+
+nsresult TestStunServer::SetResponseAddr(nr_transport_addr *addr) {
+ delete response_addr_;
+
+ response_addr_ = new nr_transport_addr();
+
+ int r = nr_transport_addr_copy(response_addr_, addr);
+ if (r)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+nsresult TestStunServer::SetResponseAddr(const std::string& addr,
+ uint16_t port) {
+ nr_transport_addr addr2;
+
+ int r = nr_str_port_to_transport_addr(addr.c_str(),
+ port, IPPROTO_UDP,
+ &addr2);
+ if (r)
+ return NS_ERROR_FAILURE;
+
+ return SetResponseAddr(&addr2);
+}
+
+void TestStunServer::Reset() {
+ delay_ms_ = 0;
+ if (timer_handle_) {
+ NR_async_timer_cancel(timer_handle_);
+ timer_handle_ = nullptr;
+ }
+ delete response_addr_;
+ response_addr_ = nullptr;
+ received_ct_.clear();
+}
+
+
+// TestStunTcpServer
+
+void TestStunTcpServer::ConfigurePort(uint16_t port) {
+ instance_port = port;
+}
+
+TestStunTcpServer* TestStunTcpServer::GetInstance(int address_family) {
+ switch (address_family) {
+ case AF_INET:
+ if (!instance)
+ instance = Create(address_family).release();
+
+ MOZ_ASSERT(instance);
+ return instance;
+ case AF_INET6:
+ if (!instance6)
+ instance6 = Create(address_family).release();
+
+ return instance6;
+ default:
+ MOZ_CRASH();
+ }
+}
+
+void TestStunTcpServer::ShutdownInstance() {
+ delete instance;
+ instance = nullptr;
+ delete instance6;
+ instance6 = nullptr;
+}
+
+int TestStunTcpServer::TryOpenListenSocket(nr_local_addr *addr, uint16_t port) {
+
+ addr->addr.protocol=IPPROTO_TCP;
+
+ int r = SetInternalPort(addr, port);
+
+ if (r)
+ return r;
+
+ nr_socket *sock;
+ if (nr_socket_local_create(nullptr, &addr->addr, &sock)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket");
+ return R_ALREADY;
+ }
+
+ if (nr_socket_buffered_stun_create(sock, 2048, TURN_TCP_FRAMING, &listen_sock_)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create listen tcp socket");
+ return R_ALREADY;
+ }
+
+ if(nr_socket_listen(listen_sock_, 10)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't listen on socket");
+ return R_ALREADY;
+ }
+
+ return 0;
+}
+
+nr_socket* TestStunTcpServer::GetReceivingSocket(NR_SOCKET s) {
+ return connections_[s];
+}
+
+nr_socket* TestStunTcpServer::GetSendingSocket(nr_socket *sock) {
+ return sock;
+}
+
+void TestStunTcpServer::accept_cb(NR_SOCKET s, int how, void *cb_arg) {
+ TestStunTcpServer *server = static_cast<TestStunTcpServer*>(cb_arg);
+ nr_socket *newsock, *bufsock, *wrapsock;
+ nr_transport_addr remote_addr;
+ NR_SOCKET fd;
+
+ /* rearm */
+ NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, cb_arg);
+
+ /* accept */
+ if (nr_socket_accept(server->listen_sock_, &remote_addr, &newsock)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't accept incoming tcp connection");
+ return;
+ }
+
+ if(nr_socket_buffered_stun_create(newsock, 2048, TURN_TCP_FRAMING, &bufsock)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create connected tcp socket");
+ nr_socket_destroy(&newsock);
+ return;
+ }
+
+ nr_socket_buffered_set_connected_to(bufsock, &remote_addr);
+
+ if(nr_socket_wrapped_create(bufsock, &wrapsock)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't wrap connected tcp socket");
+ nr_socket_destroy(&bufsock);
+ return;
+ }
+
+ if(nr_socket_getfd(wrapsock, &fd)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get fd from connected tcp socket");
+ nr_socket_destroy(&wrapsock);
+ return;
+ }
+
+ server->connections_[fd] = wrapsock;
+
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server);
+}
+
+ UniquePtr<TestStunTcpServer> TestStunTcpServer::Create(int address_family) {
+ NR_reg_init(NR_REG_MODE_LOCAL);
+
+ UniquePtr<TestStunTcpServer> server(new TestStunTcpServer());
+
+ if (server->Initialize(address_family)) {
+ return nullptr;
+ }
+
+ NR_SOCKET fd;
+ if(nr_socket_getfd(server->listen_sock_, &fd)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get tcp fd");
+ return nullptr;
+ }
+
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunTcpServer::accept_cb, server.get());
+
+ return server;
+}
+
+TestStunTcpServer::~TestStunTcpServer() {
+ for (auto it = connections_.begin(); it != connections_.end();) {
+ NR_ASYNC_CANCEL(it->first, NR_ASYNC_WAIT_READ);
+ nr_socket_destroy(&it->second);
+ connections_.erase(it++);
+ }
+}
+
+} // close namespace
diff --git a/media/mtransport/test/stunserver.h b/media/mtransport/test/stunserver.h
new file mode 100644
index 000000000..33f1b2a31
--- /dev/null
+++ b/media/mtransport/test/stunserver.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef stunserver_h__
+#define stunserver_h__
+
+#include <map>
+#include <string>
+#include "prio.h"
+#include "nsError.h"
+#include "mozilla/UniquePtr.h"
+
+typedef struct nr_stun_server_ctx_ nr_stun_server_ctx;
+typedef struct nr_socket_ nr_socket;
+typedef struct nr_local_addr_ nr_local_addr;
+
+
+namespace mozilla {
+
+class TestStunServer {
+ public:
+ // Generally, you should only call API in this class from the same thread that
+ // the initial |GetInstance| call was made from.
+ static TestStunServer *GetInstance(int address_family = AF_INET);
+ static void ShutdownInstance();
+ // |ConfigurePort| will only have an effect if called before the first call
+ // to |GetInstance| (possibly following a |ShutdownInstance| call)
+ static void ConfigurePort(uint16_t port);
+ // AF_INET, AF_INET6
+ static UniquePtr<TestStunServer> Create(int address_family);
+
+ virtual ~TestStunServer();
+
+ void SetActive(bool active);
+ void SetDelay(uint32_t delay_ms);
+ void SetDropInitialPackets(uint32_t count);
+ const std::string& addr() const { return listen_addr_; }
+ uint16_t port() const { return listen_port_; }
+
+ // These should only be called from the same thread as the initial
+ // |GetInstance| call.
+ nsresult SetResponseAddr(nr_transport_addr *addr);
+ nsresult SetResponseAddr(const std::string& addr, uint16_t port);
+
+ void Reset();
+
+ static const size_t max_stun_message_size = 4096;
+
+ virtual nr_socket* GetReceivingSocket(NR_SOCKET s);
+ virtual nr_socket* GetSendingSocket(nr_socket *sock);
+
+ protected:
+ TestStunServer()
+ : listen_port_(0),
+ listen_sock_(nullptr),
+ send_sock_(nullptr),
+ stun_server_(nullptr),
+ active_(true),
+ delay_ms_(0),
+ initial_ct_(0),
+ response_addr_(nullptr),
+ timer_handle_(nullptr) {}
+
+ int SetInternalPort(nr_local_addr *addr, uint16_t port);
+ int Initialize(int address_family);
+
+ static void readable_cb(NR_SOCKET sock, int how, void *cb_arg);
+
+ private:
+ void Process(const uint8_t *msg, size_t len, nr_transport_addr *addr_in, nr_socket *sock);
+ virtual int TryOpenListenSocket(nr_local_addr *addr, uint16_t port);
+ static void process_cb(NR_SOCKET sock, int how, void *cb_arg);
+
+ protected:
+ std::string listen_addr_;
+ uint16_t listen_port_;
+ nr_socket *listen_sock_;
+ nr_socket *send_sock_;
+ nr_stun_server_ctx *stun_server_;
+ private:
+ bool active_;
+ uint32_t delay_ms_;
+ uint32_t initial_ct_;
+ nr_transport_addr *response_addr_;
+ void *timer_handle_;
+ std::map<std::string, uint32_t> received_ct_;
+
+ static TestStunServer *instance;
+ static TestStunServer *instance6;
+ static uint16_t instance_port;
+};
+
+class TestStunTcpServer: public TestStunServer {
+ public:
+ static TestStunTcpServer *GetInstance(int address_family);
+ static void ShutdownInstance();
+ static void ConfigurePort(uint16_t port);
+ virtual ~TestStunTcpServer();
+
+ virtual nr_socket* GetReceivingSocket(NR_SOCKET s);
+ virtual nr_socket* GetSendingSocket(nr_socket *sock);
+
+ protected:
+ TestStunTcpServer() {}
+ static void accept_cb(NR_SOCKET sock, int how, void *cb_arg);
+
+ private:
+ virtual int TryOpenListenSocket(nr_local_addr *addr, uint16_t port);
+ static UniquePtr<TestStunTcpServer> Create(int address_family);
+
+ static TestStunTcpServer *instance;
+ static TestStunTcpServer *instance6;
+ static uint16_t instance_port;
+
+ std::map<NR_SOCKET, nr_socket*> connections_;
+};
+} // End of namespace mozilla
+#endif
diff --git a/media/mtransport/test/test_nr_socket_unittest.cpp b/media/mtransport/test/test_nr_socket_unittest.cpp
new file mode 100644
index 000000000..5e0a396d3
--- /dev/null
+++ b/media/mtransport/test/test_nr_socket_unittest.cpp
@@ -0,0 +1,878 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: bcampen@mozilla.com
+
+extern "C" {
+#include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE
+#include "stun_util.h"
+#include "nr_api.h"
+#include "async_wait.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_hint.h"
+#include "local_addr.h"
+#include "registry.h"
+}
+
+#include "test_nr_socket.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsAutoPtr.h"
+#include "runnable_utils.h"
+
+#include <vector>
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+#define DATA_BUF_SIZE 1024
+
+namespace mozilla {
+
+class TestNrSocketTest : public MtransportTest {
+ public:
+ TestNrSocketTest() :
+ MtransportTest(),
+ wait_done_for_main_(false),
+ sts_(),
+ public_addrs_(),
+ private_addrs_(),
+ nats_() {
+ }
+
+ void SetUp() override {
+ MtransportTest::SetUp();
+
+ // Get the transport service as a dispatch target
+ nsresult rv;
+ sts_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ EXPECT_TRUE(NS_SUCCEEDED(rv)) << "Failed to get STS: " << (int)rv;
+ }
+
+ void TearDown() override {
+ sts_->Dispatch(WrapRunnable(this, &TestNrSocketTest::TearDown_s),
+ NS_DISPATCH_SYNC);
+
+ MtransportTest::TearDown();
+ }
+
+ void TearDown_s() {
+ public_addrs_.clear();
+ private_addrs_.clear();
+ nats_.clear();
+ sts_ = nullptr;
+ }
+
+ RefPtr<TestNrSocket> CreateTestNrSocket_s(const char *ip_str,
+ int proto,
+ TestNat *nat) {
+ // If no nat is supplied, we create a default NAT which is disabled. This
+ // is how we simulate a non-natted socket.
+ RefPtr<TestNrSocket> sock(new TestNrSocket(nat ? nat : new TestNat));
+ nr_transport_addr address;
+ nr_str_port_to_transport_addr(ip_str, 0, proto, &address);
+ int r = sock->create(&address);
+ if (r) {
+ return nullptr;
+ }
+ return sock;
+ }
+
+ void CreatePublicAddrs(size_t count,
+ const char *ip_str = "127.0.0.1",
+ int proto = IPPROTO_UDP) {
+ sts_->Dispatch(
+ WrapRunnable(this,
+ &TestNrSocketTest::CreatePublicAddrs_s,
+ count,
+ ip_str,
+ proto),
+ NS_DISPATCH_SYNC);
+ }
+
+ void CreatePublicAddrs_s(size_t count, const char* ip_str, int proto) {
+ while (count--) {
+ auto sock = CreateTestNrSocket_s(ip_str, proto, nullptr);
+ ASSERT_TRUE(sock) << "Failed to create socket";
+ public_addrs_.push_back(sock);
+ }
+ }
+
+ RefPtr<TestNat> CreatePrivateAddrs(size_t size,
+ const char* ip_str = "127.0.0.1",
+ int proto = IPPROTO_UDP) {
+ RefPtr<TestNat> result;
+ sts_->Dispatch(
+ WrapRunnableRet(&result,
+ this,
+ &TestNrSocketTest::CreatePrivateAddrs_s,
+ size,
+ ip_str,
+ proto),
+ NS_DISPATCH_SYNC);
+ return result;
+ }
+
+ RefPtr<TestNat> CreatePrivateAddrs_s(size_t count,
+ const char* ip_str,
+ int proto) {
+ RefPtr<TestNat> nat(new TestNat);
+ while (count--) {
+ auto sock = CreateTestNrSocket_s(ip_str, proto, nat);
+ if (!sock) {
+ EXPECT_TRUE(false) << "Failed to create socket";
+ break;
+ }
+ private_addrs_.push_back(sock);
+ }
+ nat->enabled_ = true;
+ nats_.push_back(nat);
+ return nat;
+ }
+
+ bool CheckConnectivityVia(
+ TestNrSocket *from,
+ TestNrSocket *to,
+ const nr_transport_addr &via,
+ nr_transport_addr *sender_external_address = nullptr) {
+ MOZ_ASSERT(from);
+
+ if (!WaitForWriteable(from)) {
+ return false;
+ }
+
+ int result = 0;
+ sts_->Dispatch(WrapRunnableRet(&result,
+ this,
+ &TestNrSocketTest::SendData_s,
+ from,
+ via),
+ NS_DISPATCH_SYNC);
+ if (result) {
+ return false;
+ }
+
+ if (!WaitForReadable(to)) {
+ return false;
+ }
+
+ nr_transport_addr dummy_outparam;
+ if (!sender_external_address) {
+ sender_external_address = &dummy_outparam;
+ }
+
+ MOZ_ASSERT(to);
+ sts_->Dispatch(WrapRunnableRet(&result,
+ this,
+ &TestNrSocketTest::RecvData_s,
+ to,
+ sender_external_address),
+ NS_DISPATCH_SYNC);
+
+ return !result;
+ }
+
+ bool CheckConnectivity(
+ TestNrSocket *from,
+ TestNrSocket *to,
+ nr_transport_addr *sender_external_address = nullptr) {
+ nr_transport_addr destination_address;
+ int r = GetAddress(to, &destination_address);
+ if (r) {
+ return false;
+ }
+
+ return CheckConnectivityVia(from,
+ to,
+ destination_address,
+ sender_external_address);
+ }
+
+ bool CheckTcpConnectivity(TestNrSocket *from, TestNrSocket *to) {
+ NrSocketBase *accepted_sock;
+ if (!Connect(from, to, &accepted_sock)) {
+ std::cerr << "Connect failed" << std::endl;
+ return false;
+ }
+
+ // write on |from|, recv on |accepted_sock|
+ if (!WaitForWriteable(from)) {
+ std::cerr << __LINE__ << "WaitForWriteable (1) failed" << std::endl;
+ return false;
+ }
+
+ int r;
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::SendDataTcp_s,
+ from),
+ NS_DISPATCH_SYNC);
+ if (r) {
+ std::cerr << "SendDataTcp_s (1) failed" << std::endl;
+ return false;
+ }
+
+ if (!WaitForReadable(accepted_sock)) {
+ std::cerr << __LINE__ << "WaitForReadable (1) failed" << std::endl;
+ return false;
+ }
+
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::RecvDataTcp_s,
+ accepted_sock),
+ NS_DISPATCH_SYNC);
+ if (r) {
+ std::cerr << "RecvDataTcp_s (1) failed" << std::endl;
+ return false;
+ }
+
+ if (!WaitForWriteable(accepted_sock)) {
+ std::cerr << __LINE__ << "WaitForWriteable (2) failed" << std::endl;
+ return false;
+ }
+
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::SendDataTcp_s,
+ accepted_sock),
+ NS_DISPATCH_SYNC);
+ if (r) {
+ std::cerr << "SendDataTcp_s (2) failed" << std::endl;
+ return false;
+ }
+
+ if (!WaitForReadable(from)) {
+ std::cerr << __LINE__ << "WaitForReadable (2) failed" << std::endl;
+ return false;
+ }
+
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::RecvDataTcp_s,
+ from),
+ NS_DISPATCH_SYNC);
+ if (r) {
+ std::cerr << "RecvDataTcp_s (2) failed" << std::endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ int GetAddress(TestNrSocket *sock, nr_transport_addr_ *address) {
+ MOZ_ASSERT(sock);
+ MOZ_ASSERT(address);
+ int r;
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::GetAddress_s,
+ sock,
+ address),
+ NS_DISPATCH_SYNC);
+ return r;
+ }
+
+ int GetAddress_s(TestNrSocket *sock, nr_transport_addr *address) {
+ return sock->getaddr(address);
+ }
+
+ int SendData_s(TestNrSocket *from, const nr_transport_addr &to) {
+ // It is up to caller to ensure that |from| is writeable.
+ const char buf[] = "foobajooba";
+ return from->sendto(buf, sizeof(buf), 0,
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ const_cast<nr_transport_addr*>(&to));
+ }
+
+ int SendDataTcp_s(NrSocketBase *from) {
+ // It is up to caller to ensure that |from| is writeable.
+ const char buf[] = "foobajooba";
+ size_t written;
+ return from->write(buf, sizeof(buf), &written);
+ }
+
+ int RecvData_s(TestNrSocket *to, nr_transport_addr *from) {
+ // It is up to caller to ensure that |to| is readable
+ char buf[DATA_BUF_SIZE];
+ size_t len;
+ // Maybe check that data matches?
+ int r = to->recvfrom(buf, sizeof(buf), &len, 0, from);
+ if (!r && (len == 0)) {
+ r = R_INTERNAL;
+ }
+ return r;
+ }
+
+ int RecvDataTcp_s(NrSocketBase *to) {
+ // It is up to caller to ensure that |to| is readable
+ char buf[DATA_BUF_SIZE];
+ size_t len;
+ // Maybe check that data matches?
+ int r = to->read(buf, sizeof(buf), &len);
+ if (!r && (len == 0)) {
+ r = R_INTERNAL;
+ }
+ return r;
+ }
+
+ int Listen_s(TestNrSocket *to) {
+ // listen on |to|
+ int r = to->listen(1);
+ if (r) {
+ return r;
+ }
+ return 0;
+ }
+
+ int Connect_s(TestNrSocket *from, TestNrSocket *to) {
+ // connect on |from|
+ nr_transport_addr destination_address;
+ int r = to->getaddr(&destination_address);
+ if (r) {
+ return r;
+ }
+
+ r = from->connect(&destination_address);
+ if (r) {
+ return r;
+ }
+
+ return 0;
+ }
+
+ int Accept_s(TestNrSocket *to, NrSocketBase **accepted_sock) {
+ nr_socket *sock;
+ nr_transport_addr source_address;
+ int r = to->accept(&source_address, &sock);
+ if (r) {
+ return r;
+ }
+
+ *accepted_sock = reinterpret_cast<NrSocketBase*>(sock->obj);
+ return 0;
+ }
+
+ bool Connect(TestNrSocket *from,
+ TestNrSocket *to,
+ NrSocketBase **accepted_sock) {
+ int r;
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::Listen_s,
+ to),
+ NS_DISPATCH_SYNC);
+ if (r) {
+ std::cerr << "Listen_s failed: " << r << std::endl;
+ return false;
+ }
+
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::Connect_s,
+ from,
+ to),
+ NS_DISPATCH_SYNC);
+ if (r && r != R_WOULDBLOCK) {
+ std::cerr << "Connect_s failed: " << r << std::endl;
+ return false;
+ }
+
+ if (!WaitForReadable(to)) {
+ std::cerr << "WaitForReadable failed" << std::endl;
+ return false;
+ }
+
+ sts_->Dispatch(WrapRunnableRet(&r,
+ this,
+ &TestNrSocketTest::Accept_s,
+ to,
+ accepted_sock),
+ NS_DISPATCH_SYNC);
+
+ if (r) {
+ std::cerr << "Accept_s failed: " << r << std::endl;
+ return false;
+ }
+ return true;
+ }
+
+
+ bool WaitForSocketState(NrSocketBase *sock, int state) {
+ MOZ_ASSERT(sock);
+ sts_->Dispatch(WrapRunnable(this,
+ &TestNrSocketTest::WaitForSocketState_s,
+ sock,
+ state),
+ NS_DISPATCH_SYNC);
+
+ bool res;
+ WAIT_(wait_done_for_main_, 500, res);
+ wait_done_for_main_ = false;
+
+ if (!res) {
+ sts_->Dispatch(WrapRunnable(this,
+ &TestNrSocketTest::CancelWait_s,
+ sock,
+ state),
+ NS_DISPATCH_SYNC);
+ }
+
+ return res;
+ }
+
+ void WaitForSocketState_s(NrSocketBase *sock, int state) {
+ NR_ASYNC_WAIT(sock, state, &WaitDone, this);
+ }
+
+ void CancelWait_s(NrSocketBase *sock, int state) {
+ sock->cancel(state);
+ }
+
+ bool WaitForReadable(NrSocketBase *sock) {
+ return WaitForSocketState(sock, NR_ASYNC_WAIT_READ);
+ }
+
+ bool WaitForWriteable(NrSocketBase *sock) {
+ return WaitForSocketState(sock, NR_ASYNC_WAIT_WRITE);
+ }
+
+ static void WaitDone(void *sock, int how, void *test_fixture) {
+ TestNrSocketTest *test = static_cast<TestNrSocketTest*>(test_fixture);
+ test->wait_done_for_main_ = true;
+ }
+
+ // Simple busywait boolean for the test cases to spin on.
+ Atomic<bool> wait_done_for_main_;
+
+ nsCOMPtr<nsIEventTarget> sts_;
+ std::vector<RefPtr<TestNrSocket>> public_addrs_;
+ std::vector<RefPtr<TestNrSocket>> private_addrs_;
+ std::vector<RefPtr<TestNat>> nats_;
+};
+
+} // namespace mozilla
+
+using mozilla::TestNrSocketTest;
+using mozilla::TestNat;
+
+TEST_F(TestNrSocketTest, PublicConnectivity) {
+ CreatePublicAddrs(2);
+
+ ASSERT_TRUE(CheckConnectivity(public_addrs_[0], public_addrs_[1]));
+ ASSERT_TRUE(CheckConnectivity(public_addrs_[1], public_addrs_[0]));
+ ASSERT_TRUE(CheckConnectivity(public_addrs_[0], public_addrs_[0]));
+ ASSERT_TRUE(CheckConnectivity(public_addrs_[1], public_addrs_[1]));
+}
+
+TEST_F(TestNrSocketTest, PrivateConnectivity) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(2));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[1]));
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[0]));
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[0]));
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[1]));
+}
+
+TEST_F(TestNrSocketTest, NoConnectivityWithoutPinhole) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(1);
+
+ ASSERT_FALSE(CheckConnectivity(public_addrs_[0], private_addrs_[0]));
+}
+
+TEST_F(TestNrSocketTest, NoConnectivityBetweenSubnets) {
+ RefPtr<TestNat> nat1(CreatePrivateAddrs(1));
+ nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ RefPtr<TestNat> nat2(CreatePrivateAddrs(1));
+ nat2->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat2->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+
+ ASSERT_FALSE(CheckConnectivity(private_addrs_[0], private_addrs_[1]));
+ ASSERT_FALSE(CheckConnectivity(private_addrs_[1], private_addrs_[0]));
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0], private_addrs_[0]));
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[1], private_addrs_[1]));
+}
+
+TEST_F(TestNrSocketTest, FullConeAcceptIngress) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(2);
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Verify that other public IP can use the pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address));
+}
+
+TEST_F(TestNrSocketTest, FullConeOnePinhole) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(2);
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Send traffic to other public IP, verify that it uses the same pinhole
+ nr_transport_addr sender_external_address2;
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[1],
+ &sender_external_address2));
+ ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address,
+ &sender_external_address2,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ << "addr1: " << sender_external_address.as_string << " addr2: "
+ << sender_external_address2.as_string;
+}
+
+// OS 10.6 doesn't seem to allow us to open ports on 127.0.0.2, and while linux
+// does allow this, it has other behavior (see below) that prevents this test
+// from working.
+TEST_F(TestNrSocketTest, DISABLED_AddressRestrictedCone) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::ADDRESS_DEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(2, "127.0.0.1");
+ CreatePublicAddrs(1, "127.0.0.2");
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Verify that another address on the same host can use the pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Linux has a tendency to monkey around with source addresses, doing
+ // stuff like substituting 127.0.0.1 for packets sent by 127.0.0.2, and even
+ // going as far as substituting localhost for a packet sent from a real IP
+ // address when the destination is localhost. The only way to make this test
+ // work on linux is to have two real IP addresses.
+#ifndef __linux__
+ // Verify that an address on a different host can't use the pinhole
+ ASSERT_FALSE(CheckConnectivityVia(public_addrs_[2],
+ private_addrs_[0],
+ sender_external_address));
+#endif
+
+ // Send traffic to other public IP, verify that it uses the same pinhole
+ nr_transport_addr sender_external_address2;
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[1],
+ &sender_external_address2));
+ ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address,
+ &sender_external_address2,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ << "addr1: " << sender_external_address.as_string << " addr2: "
+ << sender_external_address2.as_string;
+
+ // Verify that the other public IP can now use the pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address2));
+
+ // Send traffic to other public IP, verify that it uses the same pinhole
+ nr_transport_addr sender_external_address3;
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[2],
+ &sender_external_address3));
+ ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address,
+ &sender_external_address3,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ << "addr1: " << sender_external_address.as_string << " addr2: "
+ << sender_external_address3.as_string;
+
+ // Verify that the other public IP can now use the pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[2],
+ private_addrs_[0],
+ sender_external_address3));
+}
+
+TEST_F(TestNrSocketTest, RestrictedCone) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::PORT_DEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(2);
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Verify that other public IP cannot use the pinhole
+ ASSERT_FALSE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Send traffic to other public IP, verify that it uses the same pinhole
+ nr_transport_addr sender_external_address2;
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[1],
+ &sender_external_address2));
+ ASSERT_FALSE(nr_transport_addr_cmp(&sender_external_address,
+ &sender_external_address2,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ << "addr1: " << sender_external_address.as_string << " addr2: "
+ << sender_external_address2.as_string;
+
+ // Verify that the other public IP can now use the pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address2));
+}
+
+TEST_F(TestNrSocketTest, PortDependentMappingFullCone) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::PORT_DEPENDENT;
+ CreatePublicAddrs(2);
+
+ nr_transport_addr sender_external_address0;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address0));
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address0));
+
+ // Verify that other public IP can use the pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address0));
+
+ // Send traffic to other public IP, verify that it uses a different pinhole
+ nr_transport_addr sender_external_address1;
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[1],
+ &sender_external_address1));
+ ASSERT_TRUE(nr_transport_addr_cmp(&sender_external_address0,
+ &sender_external_address1,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ << "addr1: " << sender_external_address0.as_string << " addr2: "
+ << sender_external_address1.as_string;
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address1));
+
+ // Verify that other public IP can use the original pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address1));
+}
+
+TEST_F(TestNrSocketTest, Symmetric) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::PORT_DEPENDENT;
+ nat->mapping_type_ = TestNat::PORT_DEPENDENT;
+ CreatePublicAddrs(2);
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Verify that other public IP cannot use the pinhole
+ ASSERT_FALSE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address));
+
+ // Send traffic to other public IP, verify that it uses a new pinhole
+ nr_transport_addr sender_external_address2;
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[1],
+ &sender_external_address2));
+ ASSERT_TRUE(nr_transport_addr_cmp(&sender_external_address,
+ &sender_external_address2,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+
+ // Verify that the other public IP can use the new pinhole
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[1],
+ private_addrs_[0],
+ sender_external_address2));
+}
+
+TEST_F(TestNrSocketTest, BlockUdp) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(2));
+ nat->block_udp_ = true;
+ CreatePublicAddrs(1);
+
+ nr_transport_addr sender_external_address;
+ ASSERT_FALSE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Make sure UDP behind the NAT still works
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ private_addrs_[1]));
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[1],
+ private_addrs_[0]));
+}
+
+TEST_F(TestNrSocketTest, DenyHairpinning) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(2));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(1);
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that hairpinning is disallowed
+ ASSERT_FALSE(CheckConnectivityVia(private_addrs_[1],
+ private_addrs_[0],
+ sender_external_address));
+}
+
+TEST_F(TestNrSocketTest, AllowHairpinning) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(2));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_timeout_ = 30000;
+ nat->allow_hairpinning_ = true;
+ CreatePublicAddrs(1);
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0, obtain external address
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that hairpinning is allowed
+ ASSERT_TRUE(CheckConnectivityVia(private_addrs_[1],
+ private_addrs_[0],
+ sender_external_address));
+}
+
+TEST_F(TestNrSocketTest, FullConeTimeout) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_timeout_ = 200;
+ CreatePublicAddrs(2);
+
+ nr_transport_addr sender_external_address;
+ // Open pinhole to public IP 0
+ ASSERT_TRUE(CheckConnectivity(private_addrs_[0],
+ public_addrs_[0],
+ &sender_external_address));
+
+ // Verify that return traffic works
+ ASSERT_TRUE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address));
+
+ PR_Sleep(201);
+
+ // Verify that return traffic does not work
+ ASSERT_FALSE(CheckConnectivityVia(public_addrs_[0],
+ private_addrs_[0],
+ sender_external_address));
+}
+
+TEST_F(TestNrSocketTest, PublicConnectivityTcp)
+{
+ CreatePublicAddrs(2, "127.0.0.1", IPPROTO_TCP);
+
+ ASSERT_TRUE(CheckTcpConnectivity(public_addrs_[0], public_addrs_[1]));
+}
+
+TEST_F(TestNrSocketTest, PrivateConnectivityTcp) {
+ RefPtr<TestNat> nat(CreatePrivateAddrs(2, "127.0.0.1", IPPROTO_TCP));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+
+ ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1]));
+}
+
+TEST_F(TestNrSocketTest, PrivateToPublicConnectivityTcp)
+{
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP);
+
+ ASSERT_TRUE(CheckTcpConnectivity(private_addrs_[0], public_addrs_[0]));
+}
+
+TEST_F(TestNrSocketTest, NoConnectivityBetweenSubnetsTcp)
+{
+ RefPtr<TestNat> nat1(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
+ nat1->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat1->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ RefPtr<TestNat> nat2(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
+ nat2->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat2->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+
+ ASSERT_FALSE(CheckTcpConnectivity(private_addrs_[0], private_addrs_[1]));
+}
+
+TEST_F(TestNrSocketTest, NoConnectivityPublicToPrivateTcp)
+{
+ RefPtr<TestNat> nat(CreatePrivateAddrs(1, "127.0.0.1", IPPROTO_TCP));
+ nat->filtering_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ nat->mapping_type_ = TestNat::ENDPOINT_INDEPENDENT;
+ CreatePublicAddrs(1, "127.0.0.1", IPPROTO_TCP);
+
+ ASSERT_FALSE(CheckTcpConnectivity(public_addrs_[0], private_addrs_[0]));
+}
diff --git a/media/mtransport/test/transport_unittests.cpp b/media/mtransport/test/transport_unittests.cpp
new file mode 100644
index 000000000..b0be39f06
--- /dev/null
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -0,0 +1,1344 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include <iostream>
+#include <string>
+#include <map>
+#include <algorithm>
+
+#include "mozilla/UniquePtr.h"
+
+#include "sigslot.h"
+
+#include "logging.h"
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+#include "sslproto.h"
+
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+#include "databuffer.h"
+#include "dtlsidentity.h"
+#include "nricectxhandler.h"
+#include "nricemediastream.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+#include "transportlayerdtls.h"
+#include "transportlayerice.h"
+#include "transportlayerlog.h"
+#include "transportlayerloopback.h"
+
+#include "runnable_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+MOZ_MTLOG_MODULE("mtransport")
+
+
+const uint8_t kTlsChangeCipherSpecType = 0x14;
+const uint8_t kTlsHandshakeType = 0x16;
+
+const uint8_t kTlsHandshakeCertificate = 0x0b;
+const uint8_t kTlsHandshakeServerKeyExchange = 0x0c;
+
+const uint8_t kTlsFakeChangeCipherSpec[] = {
+ kTlsChangeCipherSpecType, // Type
+ 0xfe, 0xff, // Version
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // Fictitious sequence #
+ 0x00, 0x01, // Length
+ 0x01 // Value
+};
+
+// Layer class which can't be initialized.
+class TransportLayerDummy : public TransportLayer {
+ public:
+ TransportLayerDummy(bool allow_init, bool *destroyed)
+ : allow_init_(allow_init),
+ destroyed_(destroyed) {
+ *destroyed_ = false;
+ }
+
+ virtual ~TransportLayerDummy() {
+ *destroyed_ = true;
+ }
+
+ virtual nsresult InitInternal() {
+ return allow_init_ ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len) {
+ MOZ_CRASH(); // Should never be called.
+ return 0;
+ }
+
+ TRANSPORT_LAYER_ID("lossy")
+
+ private:
+ bool allow_init_;
+ bool *destroyed_;
+};
+
+class Inspector {
+ public:
+ virtual ~Inspector() {}
+
+ virtual void Inspect(TransportLayer* layer,
+ const unsigned char *data, size_t len) = 0;
+};
+
+// Class to simulate various kinds of network lossage
+class TransportLayerLossy : public TransportLayer {
+ public:
+ TransportLayerLossy() : loss_mask_(0), packet_(0), inspector_(nullptr) {}
+ ~TransportLayerLossy () {}
+
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len) {
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "SendPacket(" << len << ")");
+
+ if (loss_mask_ & (1 << (packet_ % 32))) {
+ MOZ_MTLOG(ML_NOTICE, "Dropping packet");
+ ++packet_;
+ return len;
+ }
+ if (inspector_) {
+ inspector_->Inspect(this, data, len);
+ }
+
+ ++packet_;
+
+ return downward_->SendPacket(data, len);
+ }
+
+ void SetLoss(uint32_t packet) {
+ loss_mask_ |= (1 << (packet & 32));
+ }
+
+ void SetInspector(UniquePtr<Inspector> inspector) {
+ inspector_ = Move(inspector);
+ }
+
+ void StateChange(TransportLayer *layer, State state) {
+ TL_SET_STATE(state);
+ }
+
+ void PacketReceived(TransportLayer *layer, const unsigned char *data,
+ size_t len) {
+ SignalPacketReceived(this, data, len);
+ }
+
+ TRANSPORT_LAYER_ID("lossy")
+
+ protected:
+ virtual void WasInserted() {
+ downward_->SignalPacketReceived.
+ connect(this,
+ &TransportLayerLossy::PacketReceived);
+ downward_->SignalStateChange.
+ connect(this,
+ &TransportLayerLossy::StateChange);
+
+ TL_SET_STATE(downward_->state());
+ }
+
+ private:
+ uint32_t loss_mask_;
+ uint32_t packet_;
+ UniquePtr<Inspector> inspector_;
+};
+
+// Process DTLS Records
+#define CHECK_LENGTH(expected) \
+ do { \
+ EXPECT_GE(remaining(), expected); \
+ if (remaining() < expected) return false; \
+ } while(0)
+
+class TlsParser {
+ public:
+ TlsParser(const unsigned char *data, size_t len)
+ : buffer_(data, len), offset_(0) {}
+
+ bool Read(unsigned char* val) {
+ if (remaining() < 1) {
+ return false;
+ }
+ *val = *ptr();
+ consume(1);
+ return true;
+ }
+
+ // Read an integral type of specified width.
+ bool Read(uint32_t *val, size_t len) {
+ if (len > sizeof(uint32_t))
+ return false;
+
+ *val = 0;
+
+ for (size_t i=0; i<len; ++i) {
+ unsigned char tmp;
+
+ if (!Read(&tmp))
+ return false;
+
+ (*val) = ((*val) << 8) + tmp;
+ }
+
+ return true;
+ }
+
+ bool Read(unsigned char* val, size_t len) {
+ if (remaining() < len) {
+ return false;
+ }
+
+ if (val) {
+ memcpy(val, ptr(), len);
+ }
+ consume(len);
+
+ return true;
+ }
+
+ private:
+ size_t remaining() const { return buffer_.len() - offset_; }
+ const uint8_t *ptr() const { return buffer_.data() + offset_; }
+ void consume(size_t len) { offset_ += len; }
+
+ DataBuffer buffer_;
+ size_t offset_;
+};
+
+class DtlsRecordParser {
+ public:
+ DtlsRecordParser(const unsigned char *data, size_t len)
+ : buffer_(data, len), offset_(0) {}
+
+ bool NextRecord(uint8_t* ct, nsAutoPtr<DataBuffer>* buffer) {
+ if (!remaining())
+ return false;
+
+ CHECK_LENGTH(13U);
+ const uint8_t *ctp = reinterpret_cast<const uint8_t *>(ptr());
+ consume(11); // ct + version + length
+
+ const uint16_t *tmp = reinterpret_cast<const uint16_t*>(ptr());
+ size_t length = ntohs(*tmp);
+ consume(2);
+
+ CHECK_LENGTH(length);
+ DataBuffer* db = new DataBuffer(ptr(), length);
+ consume(length);
+
+ *ct = *ctp;
+ *buffer = db;
+
+ return true;
+ }
+
+ private:
+ size_t remaining() const { return buffer_.len() - offset_; }
+ const uint8_t *ptr() const { return buffer_.data() + offset_; }
+ void consume(size_t len) { offset_ += len; }
+
+ DataBuffer buffer_;
+ size_t offset_;
+};
+
+
+// Inspector that parses out DTLS records and passes
+// them on.
+class DtlsRecordInspector : public Inspector {
+ public:
+ virtual void Inspect(TransportLayer* layer,
+ const unsigned char *data, size_t len) {
+ DtlsRecordParser parser(data, len);
+
+ uint8_t ct;
+ nsAutoPtr<DataBuffer> buf;
+ while(parser.NextRecord(&ct, &buf)) {
+ OnRecord(layer, ct, buf->data(), buf->len());
+ }
+ }
+
+ virtual void OnRecord(TransportLayer* layer,
+ uint8_t content_type,
+ const unsigned char *record,
+ size_t len) = 0;
+};
+
+// Inspector that injects arbitrary packets based on
+// DTLS records of various types.
+class DtlsInspectorInjector : public DtlsRecordInspector {
+ public:
+ DtlsInspectorInjector(uint8_t packet_type, uint8_t handshake_type,
+ const unsigned char *data, size_t len) :
+ packet_type_(packet_type),
+ handshake_type_(handshake_type),
+ injected_(false) {
+ data_.reset(new unsigned char[len]);
+ memcpy(data_.get(), data, len);
+ len_ = len;
+ }
+
+ virtual void OnRecord(TransportLayer* layer,
+ uint8_t content_type,
+ const unsigned char *data, size_t len) {
+ // Only inject once.
+ if (injected_) {
+ return;
+ }
+
+ // Check that the first byte is as requested.
+ if (content_type != packet_type_) {
+ return;
+ }
+
+ if (handshake_type_ != 0xff) {
+ // Check that the packet is plausibly long enough.
+ if (len < 1) {
+ return;
+ }
+
+ // Check that the handshake type is as requested.
+ if (data[0] != handshake_type_) {
+ return;
+ }
+ }
+
+ layer->SendPacket(data_.get(), len_);
+ }
+
+ private:
+ uint8_t packet_type_;
+ uint8_t handshake_type_;
+ bool injected_;
+ UniquePtr<unsigned char[]> data_;
+ size_t len_;
+};
+
+// Make a copy of the first instance of a message.
+class DtlsInspectorRecordHandshakeMessage : public DtlsRecordInspector {
+ public:
+ explicit DtlsInspectorRecordHandshakeMessage(uint8_t handshake_type)
+ : handshake_type_(handshake_type),
+ buffer_() {}
+
+ virtual void OnRecord(TransportLayer* layer,
+ uint8_t content_type,
+ const unsigned char *data, size_t len) {
+ // Only do this once.
+ if (buffer_.len()) {
+ return;
+ }
+
+ // Check that the first byte is as requested.
+ if (content_type != kTlsHandshakeType) {
+ return;
+ }
+
+ TlsParser parser(data, len);
+ unsigned char message_type;
+ // Read the handshake message type.
+ if (!parser.Read(&message_type)) {
+ return;
+ }
+ if (message_type != handshake_type_) {
+ return;
+ }
+
+ uint32_t length;
+ if (!parser.Read(&length, 3)) {
+ return;
+ }
+
+ uint32_t message_seq;
+ if (!parser.Read(&message_seq, 2)) {
+ return;
+ }
+
+ uint32_t fragment_offset;
+ if (!parser.Read(&fragment_offset, 3)) {
+ return;
+ }
+
+ uint32_t fragment_length;
+ if (!parser.Read(&fragment_length, 3)) {
+ return;
+ }
+
+ if ((fragment_offset != 0) || (fragment_length != length)) {
+ // This shouldn't happen because all current tests where we
+ // are using this code don't fragment.
+ return;
+ }
+
+ buffer_.Allocate(length);
+ if (!parser.Read(buffer_.data(), length)) {
+ return;
+ }
+ }
+
+ const DataBuffer& buffer() { return buffer_; }
+
+ private:
+ uint8_t handshake_type_;
+ DataBuffer buffer_;
+};
+
+class TlsServerKeyExchangeECDHE {
+ public:
+ bool Parse(const unsigned char* data, size_t len) {
+ TlsParser parser(data, len);
+
+ uint8_t curve_type;
+ if (!parser.Read(&curve_type)) {
+ return false;
+ }
+
+ if (curve_type != 3) { // named_curve
+ return false;
+ }
+
+ uint32_t named_curve;
+ if (!parser.Read(&named_curve, 2)) {
+ return false;
+ }
+
+ uint32_t point_length;
+ if (!parser.Read(&point_length, 1)) {
+ return false;
+ }
+
+ public_key_.Allocate(point_length);
+ if (!parser.Read(public_key_.data(), point_length)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ DataBuffer public_key_;
+};
+
+namespace {
+class TransportTestPeer : public sigslot::has_slots<> {
+ public:
+ TransportTestPeer(nsCOMPtr<nsIEventTarget> target, std::string name, MtransportTestUtils* utils)
+ : name_(name), target_(target),
+ received_packets_(0),received_bytes_(0),flow_(new TransportFlow(name)),
+ loopback_(new TransportLayerLoopback()),
+ logging_(new TransportLayerLogging()),
+ lossy_(new TransportLayerLossy()),
+ dtls_(new TransportLayerDtls()),
+ identity_(DtlsIdentity::Generate()),
+ ice_ctx_(NrIceCtxHandler::Create(name,
+ name == "P2" ?
+ TransportLayerDtls::CLIENT :
+ TransportLayerDtls::SERVER)),
+ streams_(), candidates_(),
+ peer_(nullptr),
+ gathering_complete_(false),
+ enabled_cipersuites_(),
+ disabled_cipersuites_(),
+ reuse_dhe_key_(false),
+ test_utils_(utils) {
+ std::vector<NrIceStunServer> stun_servers;
+ UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(
+ std::string((char *)"stun.services.mozilla.com"), 3478));
+ stun_servers.push_back(*server);
+ EXPECT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetStunServers(stun_servers)));
+
+ dtls_->SetIdentity(identity_);
+ dtls_->SetRole(name == "P2" ?
+ TransportLayerDtls::CLIENT :
+ TransportLayerDtls::SERVER);
+
+ nsresult res = identity_->ComputeFingerprint("sha-1",
+ fingerprint_,
+ sizeof(fingerprint_),
+ &fingerprint_len_);
+ EXPECT_TRUE(NS_SUCCEEDED(res));
+ EXPECT_EQ(20u, fingerprint_len_);
+ }
+
+ ~TransportTestPeer() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &TransportTestPeer::DestroyFlow),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void DestroyFlow() {
+ if (flow_) {
+ loopback_->Disconnect();
+ flow_ = nullptr;
+ }
+ ice_ctx_ = nullptr;
+ }
+
+ void DisconnectDestroyFlow() {
+ loopback_->Disconnect();
+ disconnect_all(); // Disconnect from the signals;
+ flow_ = nullptr;
+ }
+
+ void SetDtlsAllowAll() {
+ nsresult res = dtls_->SetVerificationAllowAll();
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ void SetAlpn(std::string str, bool withDefault, std::string extra = "") {
+ std::set<std::string> alpn;
+ alpn.insert(str); // the one we want to select
+ if (!extra.empty()) {
+ alpn.insert(extra);
+ }
+ nsresult res = dtls_->SetAlpn(alpn, withDefault ? str : "");
+ ASSERT_EQ(NS_OK, res);
+ }
+
+ const std::string& GetAlpn() const {
+ return dtls_->GetNegotiatedAlpn();
+ }
+
+ void SetDtlsPeer(TransportTestPeer *peer, int digests, unsigned int damage) {
+ unsigned int mask = 1;
+
+ for (int i=0; i<digests; i++) {
+ unsigned char fingerprint_to_set[TransportLayerDtls::kMaxDigestLength];
+
+ memcpy(fingerprint_to_set,
+ peer->fingerprint_,
+ peer->fingerprint_len_);
+ if (damage & mask)
+ fingerprint_to_set[0]++;
+
+ nsresult res = dtls_->SetVerificationDigest(
+ "sha-1",
+ fingerprint_to_set,
+ peer->fingerprint_len_);
+
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+
+ mask <<= 1;
+ }
+ }
+
+ void SetupSrtp() {
+ // this mimics the setup we do elsewhere
+ std::vector<uint16_t> srtp_ciphers;
+ srtp_ciphers.push_back(SRTP_AES128_CM_HMAC_SHA1_80);
+ srtp_ciphers.push_back(SRTP_AES128_CM_HMAC_SHA1_32);
+
+ SetSrtpCiphers(srtp_ciphers);
+ }
+
+ void SetSrtpCiphers(std::vector<uint16_t>& srtp_ciphers) {
+ ASSERT_TRUE(NS_SUCCEEDED(dtls_->SetSrtpCiphers(srtp_ciphers)));
+ }
+
+ void ConnectSocket_s(TransportTestPeer *peer) {
+ nsresult res;
+ res = loopback_->Init();
+ ASSERT_EQ((nsresult)NS_OK, res);
+
+ loopback_->Connect(peer->loopback_);
+
+ ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
+ ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(logging_));
+ ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(lossy_));
+ ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(dtls_));
+
+ if (dtls_->state() != TransportLayer::TS_ERROR) {
+ // Don't execute these blocks if DTLS didn't initialize.
+ TweakCiphers(dtls_->internal_fd());
+ if (reuse_dhe_key_) {
+ // TransportLayerDtls automatically sets this pref to false
+ // so set it back for test.
+ // This is pretty gross. Dig directly into the NSS FD. The problem
+ // is that we are testing a feature which TransaportLayerDtls doesn't
+ // expose.
+ SECStatus rv = SSL_OptionSet(dtls_->internal_fd(),
+ SSL_REUSE_SERVER_ECDHE_KEY, PR_TRUE);
+ ASSERT_EQ(SECSuccess, rv);
+ }
+ }
+
+ flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+ }
+
+ void TweakCiphers(PRFileDesc* fd) {
+ for (auto it = enabled_cipersuites_.begin();
+ it != enabled_cipersuites_.end(); ++it) {
+ SSL_CipherPrefSet(fd, *it, PR_TRUE);
+ }
+ for (auto it = disabled_cipersuites_.begin();
+ it != disabled_cipersuites_.end(); ++it) {
+ SSL_CipherPrefSet(fd, *it, PR_FALSE);
+ }
+ }
+
+ void ConnectSocket(TransportTestPeer *peer) {
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnable(this, & TransportTestPeer::ConnectSocket_s,
+ peer),
+ NS_DISPATCH_SYNC);
+ }
+
+ void InitIce() {
+ nsresult res;
+
+ // Attach our slots
+ ice_ctx_->ctx()->SignalGatheringStateChange.
+ connect(this, &TransportTestPeer::GatheringStateChange);
+
+ char name[100];
+ snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(),
+ (int)streams_.size());
+
+ // Create the media stream
+ RefPtr<NrIceMediaStream> stream =
+ ice_ctx_->CreateStream(static_cast<char *>(name), 1);
+
+ ASSERT_TRUE(stream != nullptr);
+ ice_ctx_->ctx()->SetStream(streams_.size(), stream);
+ streams_.push_back(stream);
+
+ // Listen for candidates
+ stream->SignalCandidate.
+ connect(this, &TransportTestPeer::GotCandidate);
+
+ // Create the transport layer
+ ice_ = new TransportLayerIce(name);
+ ice_->SetParameters(ice_ctx_->ctx(), stream, 1);
+
+ // Assemble the stack
+ nsAutoPtr<std::queue<mozilla::TransportLayer *> > layers(
+ new std::queue<mozilla::TransportLayer *>);
+ layers->push(ice_);
+ layers->push(dtls_);
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, flow_, &TransportFlow::PushLayers, layers),
+ NS_DISPATCH_SYNC);
+
+ ASSERT_EQ((nsresult)NS_OK, res);
+
+ // Listen for media events
+ flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
+ flow_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
+
+ // Start gathering
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res,
+ ice_ctx_->ctx(),
+ &NrIceCtx::StartGathering,
+ false,
+ false),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ void ConnectIce(TransportTestPeer *peer) {
+ peer_ = peer;
+
+ // If gathering is already complete, push the candidates over
+ if (gathering_complete_)
+ GatheringComplete();
+ }
+
+ // New candidate
+ void GotCandidate(NrIceMediaStream *stream, const std::string &candidate) {
+ std::cerr << "Got candidate " << candidate << std::endl;
+ candidates_[stream->name()].push_back(candidate);
+ }
+
+ void GatheringStateChange(NrIceCtx* ctx,
+ NrIceCtx::GatheringState state) {
+ (void)ctx;
+ if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
+ GatheringComplete();
+ }
+ }
+
+ // Gathering complete, so send our candidates and start
+ // connecting on the other peer.
+ void GatheringComplete() {
+ nsresult res;
+
+ // Don't send to the other side
+ if (!peer_) {
+ gathering_complete_ = true;
+ return;
+ }
+
+ // First send attributes
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, peer_->ice_ctx_->ctx(),
+ &NrIceCtx::ParseGlobalAttributes,
+ ice_ctx_->ctx()->GetGlobalAttributes()),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+
+ for (size_t i=0; i<streams_.size(); ++i) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, peer_->streams_[i], &NrIceMediaStream::ParseAttributes,
+ candidates_[streams_[i]->name()]), NS_DISPATCH_SYNC);
+
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ // Start checks on the other peer.
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, peer_->ice_ctx_->ctx(), &NrIceCtx::StartChecks),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ TransportResult SendPacket(const unsigned char* data, size_t len) {
+ TransportResult ret;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&ret, flow_, &TransportFlow::SendPacket, data, len),
+ NS_DISPATCH_SYNC);
+
+ return ret;
+ }
+
+
+ void StateChanged(TransportFlow *flow, TransportLayer::State state) {
+ if (state == TransportLayer::TS_OPEN) {
+ std::cerr << "Now connected" << std::endl;
+ }
+ }
+
+ void PacketReceived(TransportFlow * flow, const unsigned char* data,
+ size_t len) {
+ std::cerr << "Received " << len << " bytes" << std::endl;
+ ++received_packets_;
+ received_bytes_ += len;
+ }
+
+ void SetLoss(uint32_t loss) {
+ lossy_->SetLoss(loss);
+ }
+
+ void SetCombinePackets(bool combine) {
+ loopback_->CombinePackets(combine);
+ }
+
+ void SetInspector(UniquePtr<Inspector> inspector) {
+ lossy_->SetInspector(Move(inspector));
+ }
+
+ void SetInspector(Inspector* in) {
+ UniquePtr<Inspector> inspector(in);
+
+ lossy_->SetInspector(Move(inspector));
+ }
+
+ void SetCipherSuiteChanges(const std::vector<uint16_t>& enableThese,
+ const std::vector<uint16_t>& disableThese) {
+ disabled_cipersuites_ = disableThese;
+ enabled_cipersuites_ = enableThese;
+ }
+
+ void SetReuseECDHEKey() {
+ reuse_dhe_key_ = true;
+ }
+
+ TransportLayer::State state() {
+ TransportLayer::State tstate;
+
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnableRet(&tstate, flow_, &TransportFlow::state));
+
+ return tstate;
+ }
+
+ bool connected() {
+ return state() == TransportLayer::TS_OPEN;
+ }
+
+ bool failed() {
+ return state() == TransportLayer::TS_ERROR;
+ }
+
+ size_t receivedPackets() { return received_packets_; }
+
+ size_t receivedBytes() { return received_bytes_; }
+
+ uint16_t cipherSuite() const {
+ nsresult rv;
+ uint16_t cipher;
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnableRet(&rv, dtls_, &TransportLayerDtls::GetCipherSuite,
+ &cipher));
+
+ if (NS_FAILED(rv)) {
+ return TLS_NULL_WITH_NULL_NULL; // i.e., not good
+ }
+ return cipher;
+ }
+
+ uint16_t srtpCipher() const {
+ nsresult rv;
+ uint16_t cipher;
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnableRet(&rv, dtls_, &TransportLayerDtls::GetSrtpCipher,
+ &cipher));
+ if (NS_FAILED(rv)) {
+ return 0; // the SRTP equivalent of TLS_NULL_WITH_NULL_NULL
+ }
+ return cipher;
+ }
+
+ private:
+ std::string name_;
+ nsCOMPtr<nsIEventTarget> target_;
+ size_t received_packets_;
+ size_t received_bytes_;
+ RefPtr<TransportFlow> flow_;
+ TransportLayerLoopback *loopback_;
+ TransportLayerLogging *logging_;
+ TransportLayerLossy *lossy_;
+ TransportLayerDtls *dtls_;
+ TransportLayerIce *ice_;
+ RefPtr<DtlsIdentity> identity_;
+ RefPtr<NrIceCtxHandler> ice_ctx_;
+ std::vector<RefPtr<NrIceMediaStream> > streams_;
+ std::map<std::string, std::vector<std::string> > candidates_;
+ TransportTestPeer *peer_;
+ bool gathering_complete_;
+ unsigned char fingerprint_[TransportLayerDtls::kMaxDigestLength];
+ size_t fingerprint_len_;
+ std::vector<uint16_t> enabled_cipersuites_;
+ std::vector<uint16_t> disabled_cipersuites_;
+ bool reuse_dhe_key_;
+ MtransportTestUtils* test_utils_;
+};
+
+
+class TransportTest : public MtransportTest {
+ public:
+ TransportTest() {
+ fds_[0] = nullptr;
+ fds_[1] = nullptr;
+ }
+
+ void TearDown() override {
+ delete p1_;
+ delete p2_;
+
+ // Can't detach these
+ // PR_Close(fds_[0]);
+ // PR_Close(fds_[1]);
+ MtransportTest::TearDown();
+ }
+
+ void DestroyPeerFlows() {
+ p1_->DisconnectDestroyFlow();
+ p2_->DisconnectDestroyFlow();
+ }
+
+ void SetUp() override {
+ MtransportTest::SetUp();
+
+ nsresult rv;
+ target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ Reset();
+ }
+
+ void Reset() {
+ p1_ = new TransportTestPeer(target_, "P1", test_utils_);
+ p2_ = new TransportTestPeer(target_, "P2", test_utils_);
+ }
+
+ void SetupSrtp() {
+ p1_->SetupSrtp();
+ p2_->SetupSrtp();
+ }
+
+ void SetDtlsPeer(int digests = 1, unsigned int damage = 0) {
+ p1_->SetDtlsPeer(p2_, digests, damage);
+ p2_->SetDtlsPeer(p1_, digests, damage);
+ }
+
+ void SetDtlsAllowAll() {
+ p1_->SetDtlsAllowAll();
+ p2_->SetDtlsAllowAll();
+ }
+
+ void SetAlpn(std::string first, std::string second,
+ bool withDefaults = true) {
+ if (!first.empty()) {
+ p1_->SetAlpn(first, withDefaults, "bogus");
+ }
+ if (!second.empty()) {
+ p2_->SetAlpn(second, withDefaults);
+ }
+ }
+
+ void CheckAlpn(std::string first, std::string second) {
+ ASSERT_EQ(first, p1_->GetAlpn());
+ ASSERT_EQ(second, p2_->GetAlpn());
+ }
+
+ void ConnectSocket() {
+ ConnectSocketInternal();
+ ASSERT_TRUE_WAIT(p1_->connected(), 10000);
+ ASSERT_TRUE_WAIT(p2_->connected(), 10000);
+
+ ASSERT_EQ(p1_->cipherSuite(), p2_->cipherSuite());
+ ASSERT_EQ(p1_->srtpCipher(), p2_->srtpCipher());
+ }
+
+ void ConnectSocketExpectFail() {
+ ConnectSocketInternal();
+ ASSERT_TRUE_WAIT(p1_->failed(), 10000);
+ ASSERT_TRUE_WAIT(p2_->failed(), 10000);
+ }
+
+ void ConnectSocketExpectState(TransportLayer::State s1,
+ TransportLayer::State s2) {
+ ConnectSocketInternal();
+ ASSERT_EQ_WAIT(s1, p1_->state(), 10000);
+ ASSERT_EQ_WAIT(s2, p2_->state(), 10000);
+ }
+
+ void InitIce() {
+ p1_->InitIce();
+ p2_->InitIce();
+ }
+
+ void ConnectIce() {
+ p1_->InitIce();
+ p2_->InitIce();
+ p1_->ConnectIce(p2_);
+ p2_->ConnectIce(p1_);
+ ASSERT_TRUE_WAIT(p1_->connected(), 10000);
+ ASSERT_TRUE_WAIT(p2_->connected(), 10000);
+ }
+
+ void TransferTest(size_t count, size_t bytes = 1024) {
+ unsigned char buf[bytes];
+
+ for (size_t i= 0; i<count; ++i) {
+ memset(buf, count & 0xff, sizeof(buf));
+ TransportResult rv = p1_->SendPacket(buf, sizeof(buf));
+ ASSERT_TRUE(rv > 0);
+ }
+
+ std::cerr << "Received == " << p2_->receivedPackets() << " packets" << std::endl;
+ ASSERT_TRUE_WAIT(count == p2_->receivedPackets(), 10000);
+ ASSERT_TRUE((count * sizeof(buf)) == p2_->receivedBytes());
+ }
+
+ protected:
+ void ConnectSocketInternal() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(p1_, &TransportTestPeer::ConnectSocket, p2_),
+ NS_DISPATCH_SYNC);
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(p2_, &TransportTestPeer::ConnectSocket, p1_),
+ NS_DISPATCH_SYNC);
+ }
+
+ PRFileDesc *fds_[2];
+ TransportTestPeer *p1_;
+ TransportTestPeer *p2_;
+ nsCOMPtr<nsIEventTarget> target_;
+};
+
+
+TEST_F(TransportTest, TestNoDtlsVerificationSettings) {
+ ConnectSocketExpectFail();
+}
+
+static void DisableChaCha(TransportTestPeer* peer) {
+ // On ARM, ChaCha20Poly1305 might be preferred; disable it for the tests that
+ // want to check the cipher suite. It doesn't matter which peer disables the
+ // suite, disabling on either side has the same effect.
+ std::vector<uint16_t> chachaSuites;
+ chachaSuites.push_back(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
+ chachaSuites.push_back(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256);
+ peer->SetCipherSuiteChanges(std::vector<uint16_t>(), chachaSuites);
+}
+
+TEST_F(TransportTest, TestConnect) {
+ SetDtlsPeer();
+ DisableChaCha(p1_);
+ ConnectSocket();
+
+ // check that we got the right suite
+ ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite());
+
+ // no SRTP on this one
+ ASSERT_EQ(0, p1_->srtpCipher());
+}
+
+TEST_F(TransportTest, TestConnectSrtp) {
+ SetupSrtp();
+ SetDtlsPeer();
+ DisableChaCha(p2_);
+ ConnectSocket();
+
+ ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite());
+
+ // SRTP is on
+ ASSERT_EQ(SRTP_AES128_CM_HMAC_SHA1_80, p1_->srtpCipher());
+}
+
+
+TEST_F(TransportTest, TestConnectDestroyFlowsMainThread) {
+ SetDtlsPeer();
+ ConnectSocket();
+ DestroyPeerFlows();
+}
+
+TEST_F(TransportTest, TestConnectAllowAll) {
+ SetDtlsAllowAll();
+ ConnectSocket();
+}
+
+TEST_F(TransportTest, TestConnectAlpn) {
+ SetDtlsPeer();
+ SetAlpn("a", "a");
+ ConnectSocket();
+ CheckAlpn("a", "a");
+}
+
+TEST_F(TransportTest, TestConnectAlpnMismatch) {
+ SetDtlsPeer();
+ SetAlpn("something", "different");
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnectAlpnServerDefault) {
+ SetDtlsPeer();
+ SetAlpn("def", "");
+ // server allows default, client doesn't support
+ ConnectSocket();
+ CheckAlpn("def", "");
+}
+
+TEST_F(TransportTest, TestConnectAlpnClientDefault) {
+ SetDtlsPeer();
+ SetAlpn("", "clientdef");
+ // client allows default, but server will ignore the extension
+ ConnectSocket();
+ CheckAlpn("", "clientdef");
+}
+
+TEST_F(TransportTest, TestConnectClientNoAlpn) {
+ SetDtlsPeer();
+ // Here the server has ALPN, but no default is allowed.
+ // Reminder: p1 == server, p2 == client
+ SetAlpn("server-nodefault", "", false);
+ // The server doesn't see the extension, so negotiates without it.
+ // But then the server is forced to close when it discovers that ALPN wasn't
+ // negotiated; the client sees a close.
+ ConnectSocketExpectState(TransportLayer::TS_ERROR,
+ TransportLayer::TS_CLOSED);
+}
+
+TEST_F(TransportTest, TestConnectServerNoAlpn) {
+ SetDtlsPeer();
+ SetAlpn("", "client-nodefault", false);
+ // The client aborts; the server doesn't realize this is a problem and just
+ // sees the close.
+ ConnectSocketExpectState(TransportLayer::TS_CLOSED,
+ TransportLayer::TS_ERROR);
+}
+
+TEST_F(TransportTest, TestConnectNoDigest) {
+ SetDtlsPeer(0, 0);
+
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnectBadDigest) {
+ SetDtlsPeer(1, 1);
+
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigests) {
+ SetDtlsPeer(2, 0);
+
+ ConnectSocket();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigestsFirstBad) {
+ SetDtlsPeer(2, 1);
+
+ ConnectSocket();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigestsSecondBad) {
+ SetDtlsPeer(2, 2);
+
+ ConnectSocket();
+}
+
+TEST_F(TransportTest, TestConnectTwoDigestsBothBad) {
+ SetDtlsPeer(2, 3);
+
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestConnectInjectCCS) {
+ SetDtlsPeer();
+ p2_->SetInspector(MakeUnique<DtlsInspectorInjector>(
+ kTlsHandshakeType,
+ kTlsHandshakeCertificate,
+ kTlsFakeChangeCipherSpec,
+ sizeof(kTlsFakeChangeCipherSpec)));
+
+ ConnectSocket();
+}
+
+
+TEST_F(TransportTest, TestConnectVerifyNewECDHE) {
+ SetDtlsPeer();
+ DtlsInspectorRecordHandshakeMessage *i1 = new
+ DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ p1_->SetInspector(i1);
+ ConnectSocket();
+ TlsServerKeyExchangeECDHE dhe1;
+ ASSERT_TRUE(dhe1.Parse(i1->buffer().data(), i1->buffer().len()));
+
+ Reset();
+ SetDtlsPeer();
+ DtlsInspectorRecordHandshakeMessage *i2 = new
+ DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ p1_->SetInspector(i2);
+ ConnectSocket();
+ TlsServerKeyExchangeECDHE dhe2;
+ ASSERT_TRUE(dhe2.Parse(i2->buffer().data(), i2->buffer().len()));
+
+ // Now compare these two to see if they are the same.
+ ASSERT_FALSE((dhe1.public_key_.len() == dhe2.public_key_.len()) &&
+ (!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
+ dhe1.public_key_.len())));
+}
+
+TEST_F(TransportTest, TestConnectVerifyReusedECDHE) {
+ SetDtlsPeer();
+ DtlsInspectorRecordHandshakeMessage *i1 = new
+ DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ p1_->SetInspector(i1);
+ p1_->SetReuseECDHEKey();
+ ConnectSocket();
+ TlsServerKeyExchangeECDHE dhe1;
+ ASSERT_TRUE(dhe1.Parse(i1->buffer().data(), i1->buffer().len()));
+
+ Reset();
+ SetDtlsPeer();
+ DtlsInspectorRecordHandshakeMessage *i2 = new
+ DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+
+ p1_->SetInspector(i2);
+ p1_->SetReuseECDHEKey();
+
+ ConnectSocket();
+ TlsServerKeyExchangeECDHE dhe2;
+ ASSERT_TRUE(dhe2.Parse(i2->buffer().data(), i2->buffer().len()));
+
+ // Now compare these two to see if they are the same.
+ ASSERT_EQ(dhe1.public_key_.len(), dhe2.public_key_.len());
+ ASSERT_TRUE(!memcmp(dhe1.public_key_.data(), dhe2.public_key_.data(),
+ dhe1.public_key_.len()));
+}
+
+TEST_F(TransportTest, TestTransfer) {
+ SetDtlsPeer();
+ ConnectSocket();
+ TransferTest(1);
+}
+
+TEST_F(TransportTest, TestTransferMaxSize) {
+ SetDtlsPeer();
+ ConnectSocket();
+ /* transportlayerdtls uses a 9216 bytes buffer - as this test uses the
+ * loopback implementation it does not have to take into account the extra
+ * bytes added by the DTLS layer below. */
+ TransferTest(1, 9216);
+}
+
+TEST_F(TransportTest, TestTransferMultiple) {
+ SetDtlsPeer();
+ ConnectSocket();
+ TransferTest(3);
+}
+
+TEST_F(TransportTest, TestTransferCombinedPackets) {
+ SetDtlsPeer();
+ ConnectSocket();
+ p2_->SetCombinePackets(true);
+ TransferTest(3);
+}
+
+TEST_F(TransportTest, TestConnectLoseFirst) {
+ SetDtlsPeer();
+ p1_->SetLoss(0);
+ ConnectSocket();
+ TransferTest(1);
+}
+
+TEST_F(TransportTest, TestConnectIce) {
+ SetDtlsPeer();
+ ConnectIce();
+}
+
+TEST_F(TransportTest, TestTransferIceMaxSize) {
+ SetDtlsPeer();
+ ConnectIce();
+ /* nICEr and transportlayerdtls both use 9216 bytes buffers. But the DTLS
+ * layer add extra bytes to the packet, which size depends on chosen cipher
+ * etc. Sending more then 9216 bytes works, but on the receiving side the call
+ * to PR_recvfrom() will truncate any packet bigger then nICEr's buffer size
+ * of 9216 bytes, which then results in the DTLS layer discarding the packet.
+ * Therefore we leave some headroom (according to
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1214269#c29 256 bytes should
+ * be save choice) here for the DTLS bytes to make it safely into the
+ * receiving buffer in nICEr. */
+ TransferTest(1, 8960);
+}
+
+TEST_F(TransportTest, TestTransferIceMultiple) {
+ SetDtlsPeer();
+ ConnectIce();
+ TransferTest(3);
+}
+
+TEST_F(TransportTest, TestTransferIceCombinedPackets) {
+ SetDtlsPeer();
+ ConnectIce();
+ p2_->SetCombinePackets(true);
+ TransferTest(3);
+}
+
+// test the default configuration against a peer that supports only
+// one of the mandatory-to-implement suites, which should succeed
+static void ConfigureOneCipher(TransportTestPeer* peer, uint16_t suite) {
+ std::vector<uint16_t> justOne;
+ justOne.push_back(suite);
+ std::vector<uint16_t> everythingElse(SSL_GetImplementedCiphers(),
+ SSL_GetImplementedCiphers()
+ + SSL_GetNumImplementedCiphers());
+ std::remove(everythingElse.begin(), everythingElse.end(), suite);
+ peer->SetCipherSuiteChanges(justOne, everythingElse);
+}
+
+TEST_F(TransportTest, TestCipherMismatch) {
+ SetDtlsPeer();
+ ConfigureOneCipher(p1_, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
+ ConfigureOneCipher(p2_, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestCipherMandatoryOnlyGcm) {
+ SetDtlsPeer();
+ ConfigureOneCipher(p1_, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256);
+ ConnectSocket();
+ ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, p1_->cipherSuite());
+}
+
+TEST_F(TransportTest, TestCipherMandatoryOnlyCbc) {
+ SetDtlsPeer();
+ ConfigureOneCipher(p1_, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
+ ConnectSocket();
+ ASSERT_EQ(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, p1_->cipherSuite());
+}
+
+TEST_F(TransportTest, TestSrtpMismatch) {
+ std::vector<uint16_t> setA;
+ setA.push_back(SRTP_AES128_CM_HMAC_SHA1_80);
+ std::vector<uint16_t> setB;
+ setB.push_back(SRTP_AES128_CM_HMAC_SHA1_32);
+
+ p1_->SetSrtpCiphers(setA);
+ p2_->SetSrtpCiphers(setB);
+ SetDtlsPeer();
+ ConnectSocket();
+
+ ASSERT_EQ(0, p1_->srtpCipher());
+ ASSERT_EQ(0, p2_->srtpCipher());
+}
+
+// NSS doesn't support DHE suites on the server end.
+// This checks to see if we barf when that's the only option available.
+TEST_F(TransportTest, TestDheOnlyFails) {
+ SetDtlsPeer();
+
+ // p2_ is the client
+ // setting this on p1_ (the server) causes NSS to assert
+ ConfigureOneCipher(p2_, TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
+ ConnectSocketExpectFail();
+}
+
+TEST(PushTests, LayerFail) {
+ RefPtr<TransportFlow> flow = new TransportFlow();
+ nsresult rv;
+ bool destroyed1, destroyed2;
+
+ rv = flow->PushLayer(new TransportLayerDummy(true, &destroyed1));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = flow->PushLayer(new TransportLayerDummy(false, &destroyed2));
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ ASSERT_EQ(TransportLayer::TS_ERROR, flow->state());
+ ASSERT_EQ(true, destroyed1);
+ ASSERT_EQ(true, destroyed2);
+
+ rv = flow->PushLayer(new TransportLayerDummy(true, &destroyed1));
+ ASSERT_TRUE(NS_FAILED(rv));
+ ASSERT_EQ(true, destroyed1);
+}
+
+TEST(PushTests, LayersFail) {
+ RefPtr<TransportFlow> flow = new TransportFlow();
+ nsresult rv;
+ bool destroyed1, destroyed2, destroyed3;
+
+ rv = flow->PushLayer(new TransportLayerDummy(true, &destroyed1));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ nsAutoPtr<std::queue<TransportLayer *> > layers(
+ new std::queue<TransportLayer *>());
+
+ layers->push(new TransportLayerDummy(true, &destroyed2));
+ layers->push(new TransportLayerDummy(false, &destroyed3));
+
+ rv = flow->PushLayers(layers);
+ ASSERT_TRUE(NS_FAILED(rv));
+
+ ASSERT_EQ(TransportLayer::TS_ERROR, flow->state());
+ ASSERT_EQ(true, destroyed1);
+ ASSERT_EQ(true, destroyed2);
+ ASSERT_EQ(true, destroyed3);
+
+ layers = new std::queue<TransportLayer *>();
+ layers->push(new TransportLayerDummy(true, &destroyed2));
+ layers->push(new TransportLayerDummy(true, &destroyed3));
+ rv = flow->PushLayers(layers);
+
+ ASSERT_TRUE(NS_FAILED(rv));
+ ASSERT_EQ(true, destroyed2);
+ ASSERT_EQ(true, destroyed3);
+}
+
+} // end namespace
diff --git a/media/mtransport/test/turn_unittest.cpp b/media/mtransport/test/turn_unittest.cpp
new file mode 100644
index 000000000..19fe8515c
--- /dev/null
+++ b/media/mtransport/test/turn_unittest.cpp
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+// Some code copied from nICEr. License is:
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdlib.h>
+#include <iostream>
+
+#include "sigslot.h"
+
+#include "logging.h"
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+#include "runnable_utils.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+#define USE_TURN
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "registry.h"
+#include "async_timer.h"
+#include "r_crc32.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "nr_socket_buffered_stun.h"
+#include "stun_client_ctx.h"
+#include "turn_client_ctx.h"
+}
+
+#include "nricemediastream.h"
+#include "nricectxhandler.h"
+
+
+using namespace mozilla;
+
+static std::string kDummyTurnServer("192.0.2.1"); // From RFC 5737
+
+class TurnClient : public MtransportTest {
+ public:
+ TurnClient()
+ : MtransportTest(),
+ real_socket_(nullptr),
+ net_socket_(nullptr),
+ buffered_socket_(nullptr),
+ net_fd_(nullptr),
+ turn_ctx_(nullptr),
+ allocated_(false),
+ received_(0),
+ protocol_(IPPROTO_UDP) {
+ }
+
+ ~TurnClient() {
+ }
+
+ static void SetUpTestCase() {
+ NrIceCtx::InitializeGlobals(false, false, false);
+ }
+
+ void SetTcp() {
+ protocol_ = IPPROTO_TCP;
+ }
+
+ void Init_s() {
+ int r;
+ nr_transport_addr addr;
+ r = nr_ip4_port_to_transport_addr(0, 0, protocol_, &addr);
+ ASSERT_EQ(0, r);
+
+ r = nr_socket_local_create(nullptr, &addr, &real_socket_);
+ ASSERT_EQ(0, r);
+
+ if (protocol_ == IPPROTO_TCP) {
+ int r =
+ nr_socket_buffered_stun_create(real_socket_, 100000, TURN_TCP_FRAMING,
+ &buffered_socket_);
+ ASSERT_EQ(0, r);
+ net_socket_ = buffered_socket_;
+ } else {
+ net_socket_ = real_socket_;
+ }
+
+ r = nr_str_port_to_transport_addr(turn_server_.c_str(), 3478,
+ protocol_, &addr);
+ ASSERT_EQ(0, r);
+
+ std::vector<unsigned char> password_vec(
+ turn_password_.begin(), turn_password_.end());
+ Data password;
+ INIT_DATA(password, &password_vec[0], password_vec.size());
+ r = nr_turn_client_ctx_create("test", net_socket_,
+ turn_user_.c_str(),
+ &password,
+ &addr, &turn_ctx_);
+ ASSERT_EQ(0, r);
+
+ r = nr_socket_getfd(net_socket_, &net_fd_);
+ ASSERT_EQ(0, r);
+
+ NR_ASYNC_WAIT(net_fd_, NR_ASYNC_WAIT_READ, socket_readable_cb,
+ (void *)this);
+ }
+
+ void TearDown_s() {
+ nr_turn_client_ctx_destroy(&turn_ctx_);
+ if (net_fd_) {
+ NR_ASYNC_CANCEL(net_fd_, NR_ASYNC_WAIT_READ);
+ }
+
+ nr_socket_destroy(&buffered_socket_);
+ }
+
+ void TearDown() {
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnable(this, &TurnClient::TearDown_s),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Allocate_s() {
+ Init_s();
+ ASSERT_TRUE(turn_ctx_);
+
+ int r = nr_turn_client_allocate(turn_ctx_,
+ allocate_success_cb,
+ this);
+ ASSERT_EQ(0, r);
+ }
+
+ void Allocate(bool expect_success=true) {
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnable(this, &TurnClient::Allocate_s),
+ NS_DISPATCH_SYNC);
+
+ if (expect_success) {
+ ASSERT_TRUE_WAIT(allocated_, 5000);
+ }
+ else {
+ PR_Sleep(10000);
+ ASSERT_FALSE(allocated_);
+ }
+ }
+
+ void Allocated() {
+ if (turn_ctx_->state!=NR_TURN_CLIENT_STATE_ALLOCATED) {
+ std::cerr << "Allocation failed" << std::endl;
+ return;
+ }
+ allocated_ = true;
+
+ int r;
+ nr_transport_addr addr;
+
+ r = nr_turn_client_get_relayed_address(turn_ctx_, &addr);
+ ASSERT_EQ(0, r);
+
+ relay_addr_ = addr.as_string;
+
+ std::cerr << "Allocation succeeded with addr=" << relay_addr_ << std::endl;
+ }
+
+ void Deallocate_s() {
+ ASSERT_TRUE(turn_ctx_);
+
+ std::cerr << "De-Allocating..." << std::endl;
+ int r = nr_turn_client_deallocate(turn_ctx_);
+ ASSERT_EQ(0, r);
+ }
+
+ void Deallocate() {
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnable(this, &TurnClient::Deallocate_s),
+ NS_DISPATCH_SYNC);
+ }
+
+ void RequestPermission_s(const std::string& target) {
+ nr_transport_addr addr;
+ int r;
+
+ // Expected pattern here is "IP4:127.0.0.1:3487"
+ ASSERT_EQ(0, target.compare(0, 4, "IP4:"));
+
+ size_t offset = target.rfind(':');
+ ASSERT_NE(std::string::npos, offset);
+
+ std::string host = target.substr(4, offset - 4);
+ std::string port = target.substr(offset + 1);
+
+ r = nr_str_port_to_transport_addr(host.c_str(),
+ atoi(port.c_str()),
+ IPPROTO_UDP,
+ &addr);
+ ASSERT_EQ(0, r);
+
+ r = nr_turn_client_ensure_perm(turn_ctx_, &addr);
+ ASSERT_EQ(0, r);
+ }
+
+ void RequestPermission(const std::string& target) {
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnable(this, &TurnClient::RequestPermission_s, target),
+ NS_DISPATCH_SYNC);
+
+ }
+
+ void Readable(NR_SOCKET s, int how, void *arg) {
+ // Re-arm
+ std::cerr << "Socket is readable" << std::endl;
+ NR_ASYNC_WAIT(s, how, socket_readable_cb, arg);
+
+ UCHAR buf[8192];
+ size_t len_s;
+ nr_transport_addr addr;
+
+ int r = nr_socket_recvfrom(net_socket_, buf, sizeof(buf), &len_s, 0, &addr);
+ if (r) {
+ std::cerr << "Error reading from socket" << std::endl;
+ return;
+ }
+
+ ASSERT_LT(len_s, (size_t)INT_MAX);
+ int len = (int)len_s;
+
+ if (nr_is_stun_response_message(buf, len)) {
+ std::cerr << "STUN response" << std::endl;
+ r = nr_turn_client_process_response(turn_ctx_, buf, len, &addr);
+
+ if (r && r != R_REJECTED && r != R_RETRY) {
+ std::cerr << "Error processing STUN: " << r << std::endl;
+ }
+ } else if (nr_is_stun_indication_message(buf, len)) {
+ std::cerr << "STUN indication" << std::endl;
+
+ /* Process the indication */
+ unsigned char data[NR_STUN_MAX_MESSAGE_SIZE];
+ size_t datal;
+ nr_transport_addr remote_addr;
+
+ r = nr_turn_client_parse_data_indication(turn_ctx_, &addr,
+ buf, len,
+ data, &datal, sizeof(data),
+ &remote_addr);
+ ASSERT_EQ(0, r);
+ std::cerr << "Received " << datal << " bytes from "
+ << remote_addr.as_string << std::endl;
+
+ received_ += datal;
+
+ for (size_t i=0; i < datal; i++) {
+ ASSERT_EQ(i & 0xff, data[i]);
+ }
+ }
+ else {
+ if (nr_is_stun_message(buf, len)) {
+ std::cerr << "STUN message of unexpected type" << std::endl;
+ } else {
+ std::cerr << "Not a STUN message" << std::endl;
+ }
+ return;
+ }
+ }
+
+ void SendTo_s(const std::string& target, int expect_return) {
+ nr_transport_addr addr;
+ int r;
+
+ // Expected pattern here is "IP4:127.0.0.1:3487"
+ ASSERT_EQ(0, target.compare(0, 4, "IP4:"));
+
+ size_t offset = target.rfind(':');
+ ASSERT_NE(std::string::npos, offset);
+
+ std::string host = target.substr(4, offset - 4);
+ std::string port = target.substr(offset + 1);
+
+ r = nr_str_port_to_transport_addr(host.c_str(),
+ atoi(port.c_str()),
+ IPPROTO_UDP,
+ &addr);
+ ASSERT_EQ(0, r);
+
+ unsigned char test[100];
+ for (size_t i=0; i<sizeof(test); i++) {
+ test[i] = i & 0xff;
+ }
+
+ std::cerr << "Sending test message to " << target << " ..." << std::endl;
+
+ r = nr_turn_client_send_indication(turn_ctx_,
+ test, sizeof(test), 0,
+ &addr);
+ if (expect_return >= 0) {
+ ASSERT_EQ(expect_return, r);
+ }
+ }
+
+ void SendTo(const std::string& target, int expect_return=0) {
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnable(this, &TurnClient::SendTo_s, target,
+ expect_return),
+ NS_DISPATCH_SYNC);
+ }
+
+ int received() const { return received_; }
+
+ static void socket_readable_cb(NR_SOCKET s, int how, void *arg) {
+ static_cast<TurnClient *>(arg)->Readable(s, how, arg);
+ }
+
+ static void allocate_success_cb(NR_SOCKET s, int how, void *arg){
+ static_cast<TurnClient *>(arg)->Allocated();
+ }
+
+ protected:
+ std::string turn_server_;
+ nr_socket *real_socket_;
+ nr_socket *net_socket_;
+ nr_socket *buffered_socket_;
+ NR_SOCKET net_fd_;
+ nr_turn_client_ctx *turn_ctx_;
+ std::string relay_addr_;
+ bool allocated_;
+ int received_;
+ int protocol_;
+};
+
+TEST_F(TurnClient, Allocate) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ Allocate();
+}
+
+TEST_F(TurnClient, AllocateTcp) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ SetTcp();
+ Allocate();
+}
+
+TEST_F(TurnClient, AllocateAndHold) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ Allocate();
+ PR_Sleep(20000);
+ ASSERT_TRUE(turn_ctx_->state == NR_TURN_CLIENT_STATE_ALLOCATED);
+}
+
+TEST_F(TurnClient, SendToSelf) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ Allocate();
+ SendTo(relay_addr_);
+ ASSERT_TRUE_WAIT(received() == 100, 5000);
+ SendTo(relay_addr_);
+ ASSERT_TRUE_WAIT(received() == 200, 1000);
+}
+
+
+TEST_F(TurnClient, SendToSelfTcp) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ SetTcp();
+ Allocate();
+ SendTo(relay_addr_);
+ ASSERT_TRUE_WAIT(received() == 100, 5000);
+ SendTo(relay_addr_);
+ ASSERT_TRUE_WAIT(received() == 200, 1000);
+}
+
+TEST_F(TurnClient, PermissionDenied) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ Allocate();
+ RequestPermission(relay_addr_);
+ PR_Sleep(1000);
+
+ /* Fake a 403 response */
+ nr_turn_permission *perm;
+ perm = STAILQ_FIRST(&turn_ctx_->permissions);
+ ASSERT_TRUE(perm);
+ while (perm) {
+ perm->stun->last_error_code = 403;
+ std::cerr << "Set 403's on permission" << std::endl;
+ perm = STAILQ_NEXT(perm, entry);
+ }
+
+ SendTo(relay_addr_, R_NOT_PERMITTED);
+ ASSERT_TRUE(received() == 0);
+
+ //TODO: We should check if we can still send to a second destination, but
+ // we would need a second TURN client as one client can only handle one
+ // allocation (maybe as part of bug 1128128 ?).
+}
+
+TEST_F(TurnClient, DeallocateReceiveFailure) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ Allocate();
+ SendTo(relay_addr_);
+ ASSERT_TRUE_WAIT(received() == 100, 5000);
+ Deallocate();
+ turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED;
+ SendTo(relay_addr_);
+ PR_Sleep(1000);
+ ASSERT_TRUE(received() == 100);
+}
+
+TEST_F(TurnClient, DeallocateReceiveFailureTcp) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ SetTcp();
+ Allocate();
+ SendTo(relay_addr_);
+ ASSERT_TRUE_WAIT(received() == 100, 5000);
+ Deallocate();
+ turn_ctx_->state = NR_TURN_CLIENT_STATE_ALLOCATED;
+ /* Either the connection got closed by the TURN server already, then the send
+ * is going to fail, which we simply ignore. Or the connection is still alive
+ * and we cand send the data, but it should not get forwarded to us. In either
+ * case we should not receive more data. */
+ SendTo(relay_addr_, -1);
+ PR_Sleep(1000);
+ ASSERT_TRUE(received() == 100);
+}
+
+TEST_F(TurnClient, AllocateDummyServer) {
+ if (WarnIfTurnNotConfigured())
+ return;
+
+ turn_server_ = kDummyTurnServer;
+ Allocate(false);
+}
diff --git a/media/mtransport/test_nr_socket.cpp b/media/mtransport/test_nr_socket.cpp
new file mode 100644
index 000000000..5f7d69a78
--- /dev/null
+++ b/media/mtransport/test_nr_socket.cpp
@@ -0,0 +1,892 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+*/
+
+/*
+Based partially on original code from nICEr and nrappkit.
+
+nICEr copyright:
+
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+nrappkit copyright:
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Thu Dec 20 20:14:49 2001
+*/
+
+// Original author: bcampen@mozilla.com [:bwc]
+
+extern "C" {
+#include "stun_msg.h" // for NR_STUN_MAX_MESSAGE_SIZE
+#include "nr_api.h"
+#include "async_wait.h"
+#include "async_timer.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_hint.h"
+#include "transport_addr.h"
+}
+
+#include "mozilla/RefPtr.h"
+#include "test_nr_socket.h"
+#include "runnable_utils.h"
+
+namespace mozilla {
+
+static int test_nat_socket_create(void *obj,
+ nr_transport_addr *addr,
+ nr_socket **sockp) {
+ RefPtr<NrSocketBase> sock = new TestNrSocket(static_cast<TestNat*>(obj));
+
+ int r, _status;
+
+ r = sock->create(addr);
+ if (r)
+ ABORT(r);
+
+ r = nr_socket_create_int(static_cast<void *>(sock),
+ sock->vtbl(), sockp);
+ if (r)
+ ABORT(r);
+
+ _status = 0;
+
+ {
+ // We will release this reference in destroy(), not exactly the normal
+ // ownership model, but it is what it is.
+ NrSocketBase *dummy = sock.forget().take();
+ (void)dummy;
+ }
+
+abort:
+ return _status;
+}
+
+static int test_nat_socket_factory_destroy(void **obj) {
+ TestNat *nat = static_cast<TestNat*>(*obj);
+ *obj = nullptr;
+ nat->Release();
+ return 0;
+}
+
+static nr_socket_factory_vtbl test_nat_socket_factory_vtbl = {
+ test_nat_socket_create,
+ test_nat_socket_factory_destroy
+};
+
+/* static */
+TestNat::NatBehavior
+TestNat::ToNatBehavior(const std::string& type) {
+ if (!type.compare("ENDPOINT_INDEPENDENT")) {
+ return TestNat::ENDPOINT_INDEPENDENT;
+ } else if (!type.compare("ADDRESS_DEPENDENT")) {
+ return TestNat::ADDRESS_DEPENDENT;
+ } else if (!type.compare("PORT_DEPENDENT")) {
+ return TestNat::PORT_DEPENDENT;
+ }
+
+ MOZ_ASSERT(false, "Invalid NAT behavior");
+ return TestNat::ENDPOINT_INDEPENDENT;
+}
+
+bool TestNat::has_port_mappings() const {
+ for (TestNrSocket *sock : sockets_) {
+ if (sock->has_port_mappings()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TestNat::is_my_external_tuple(const nr_transport_addr &addr) const {
+ for (TestNrSocket *sock : sockets_) {
+ if (sock->is_my_external_tuple(addr)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool TestNat::is_an_internal_tuple(const nr_transport_addr &addr) const {
+ for (TestNrSocket *sock : sockets_) {
+ nr_transport_addr addr_behind_nat;
+ if (sock->getaddr(&addr_behind_nat)) {
+ MOZ_CRASH("TestNrSocket::getaddr failed!");
+ }
+
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&addr),
+ &addr_behind_nat,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int TestNat::create_socket_factory(nr_socket_factory **factorypp) {
+ int r = nr_socket_factory_create_int(this,
+ &test_nat_socket_factory_vtbl,
+ factorypp);
+ if (!r) {
+ AddRef();
+ }
+ return r;
+}
+
+TestNrSocket::TestNrSocket(TestNat *nat)
+ : nat_(nat),
+ timer_handle_(nullptr) {
+ nat_->insert_socket(this);
+}
+
+TestNrSocket::~TestNrSocket() {
+ nat_->erase_socket(this);
+}
+
+RefPtr<NrSocketBase> TestNrSocket::create_external_socket(
+ const nr_transport_addr &dest_addr) const {
+ MOZ_ASSERT(nat_->enabled_);
+ MOZ_ASSERT(!nat_->is_an_internal_tuple(dest_addr));
+
+ int r;
+ nr_transport_addr nat_external_addr;
+
+ // Open the socket on an arbitrary port, on the same address.
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ if ((r = nr_transport_addr_copy(
+ &nat_external_addr,
+ const_cast<nr_transport_addr*>(&internal_socket_->my_addr())))) {
+ r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_copy: %d",
+ __FUNCTION__, r);
+ return nullptr;
+ }
+
+ if ((r = nr_transport_addr_set_port(&nat_external_addr, 0))) {
+ r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in nr_transport_addr_set_port: %d",
+ __FUNCTION__, r);
+ return nullptr;
+ }
+
+ RefPtr<NrSocketBase> external_socket;
+ r = NrSocketBase::CreateSocket(&nat_external_addr, &external_socket);
+
+ if (r) {
+ r_log(LOG_GENERIC,LOG_CRIT, "%s: Failure in NrSocket::create: %d",
+ __FUNCTION__, r);
+ return nullptr;
+ }
+
+ return external_socket;
+}
+
+int TestNrSocket::create(nr_transport_addr *addr) {
+ return NrSocketBase::CreateSocket(addr, &internal_socket_);
+}
+
+int TestNrSocket::getaddr(nr_transport_addr *addrp) {
+ return internal_socket_->getaddr(addrp);
+}
+
+void TestNrSocket::close() {
+ if (timer_handle_) {
+ NR_async_timer_cancel(timer_handle_);
+ timer_handle_ = 0;
+ }
+ internal_socket_->close();
+ for (RefPtr<PortMapping>& port_mapping : port_mappings_) {
+ port_mapping->external_socket_->close();
+ }
+}
+
+int TestNrSocket::listen(int backlog) {
+ MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %s listening",
+ internal_socket_->my_addr().as_string);
+
+ return internal_socket_->listen(backlog);
+}
+
+int TestNrSocket::accept(nr_transport_addr *addrp, nr_socket **sockp) {
+ MOZ_ASSERT(internal_socket_->my_addr().protocol == IPPROTO_TCP);
+ int r = internal_socket_->accept(addrp, sockp);
+ if (r) {
+ return r;
+ }
+
+ if (nat_->enabled_ && !nat_->is_an_internal_tuple(*addrp)) {
+ nr_socket_destroy(sockp);
+ return R_IO_ERROR;
+ }
+
+ return 0;
+}
+
+void TestNrSocket::process_delayed_cb(NR_SOCKET s, int how, void *cb_arg) {
+ DeferredPacket *op = static_cast<DeferredPacket *>(cb_arg);
+ op->socket_->timer_handle_ = nullptr;
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %s sending delayed STUN response",
+ op->internal_socket_->my_addr().as_string);
+ op->internal_socket_->sendto(op->buffer_.data(), op->buffer_.len(),
+ op->flags_, &op->to_);
+
+ delete op;
+}
+
+int TestNrSocket::sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) {
+ MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
+
+ UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg));
+ if (nat_->block_stun_ &&
+ nr_is_stun_message(buf, len)) {
+ return 0;
+ }
+
+ /* TODO: improve the functionality of this in bug 1253657 */
+ if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) {
+ if (nat_->delay_stun_resp_ms_ &&
+ nr_is_stun_response_message(buf, len)) {
+ NR_ASYNC_TIMER_SET(nat_->delay_stun_resp_ms_,
+ process_delayed_cb,
+ new DeferredPacket(this, msg, len, flags, to,
+ internal_socket_),
+ &timer_handle_);
+ return 0;
+ }
+ return internal_socket_->sendto(msg, len, flags, to);
+ }
+
+ destroy_stale_port_mappings();
+
+ if (to->protocol == IPPROTO_UDP && nat_->block_udp_) {
+ // Silently eat the packet
+ return 0;
+ }
+
+ // Choose our port mapping based on our most selective criteria
+ PortMapping *port_mapping = get_port_mapping(*to,
+ std::max(nat_->filtering_type_,
+ nat_->mapping_type_));
+
+ if (!port_mapping) {
+ // See if we have already made the external socket we need to use.
+ PortMapping *similar_port_mapping =
+ get_port_mapping(*to, nat_->mapping_type_);
+ RefPtr<NrSocketBase> external_socket;
+
+ if (similar_port_mapping) {
+ external_socket = similar_port_mapping->external_socket_;
+ } else {
+ external_socket = create_external_socket(*to);
+ if (!external_socket) {
+ MOZ_ASSERT(false);
+ return R_INTERNAL;
+ }
+ }
+
+ port_mapping = create_port_mapping(*to, external_socket);
+ port_mappings_.push_back(port_mapping);
+
+ if (poll_flags() & PR_POLL_READ) {
+ // Make sure the new port mapping is ready to receive traffic if the
+ // TestNrSocket is already waiting.
+ port_mapping->async_wait(NR_ASYNC_WAIT_READ,
+ socket_readable_callback,
+ this,
+ (char*)__FUNCTION__,
+ __LINE__);
+ }
+ }
+
+ // We probably don't want to propagate the flags, since this is a simulated
+ // external IP address.
+ return port_mapping->sendto(msg, len, *to);
+}
+
+int TestNrSocket::recvfrom(void *buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) {
+ MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
+
+ int r;
+ bool ingress_allowed = false;
+
+ if (readable_socket_) {
+ // If any of the external sockets got data, see if it will be passed through
+ r = readable_socket_->recvfrom(buf, maxlen, len, 0, from);
+ readable_socket_ = nullptr;
+ if (!r) {
+ PortMapping *port_mapping_used;
+ ingress_allowed = allow_ingress(*from, &port_mapping_used);
+ if (ingress_allowed) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s via %s",
+ internal_socket_->my_addr().as_string,
+ from->as_string,
+ port_mapping_used->external_socket_->my_addr().as_string);
+ if (nat_->refresh_on_ingress_) {
+ port_mapping_used->last_used_ = PR_IntervalNow();
+ }
+ }
+ }
+ } else {
+ // If no external socket has data, see if there's any data that was sent
+ // directly to the TestNrSocket, and eat it if it isn't supposed to get
+ // through.
+ r = internal_socket_->recvfrom(buf, maxlen, len, flags, from);
+ if (!r) {
+ // We do not use allow_ingress() here because that only handles traffic
+ // landing on an external port.
+ ingress_allowed = (!nat_->enabled_ ||
+ nat_->is_an_internal_tuple(*from));
+ if (!ingress_allowed) {
+ r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
+ "Not behind the same NAT",
+ internal_socket_->my_addr().as_string,
+ from->as_string);
+ } else {
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s received from %s",
+ internal_socket_->my_addr().as_string,
+ from->as_string);
+ }
+ }
+ }
+
+ // Kinda lame that we are forced to give the app a readable callback and then
+ // say "Oh, never mind...", but the alternative is to totally decouple the
+ // callbacks from STS and the callbacks the app sets. On the bright side, this
+ // speeds up unit tests where we are verifying that ingress is forbidden,
+ // since they'll get a readable callback and then an error, instead of having
+ // to wait for a timeout.
+ if (!ingress_allowed) {
+ *len = 0;
+ r = R_WOULDBLOCK;
+ }
+
+ return r;
+}
+
+bool TestNrSocket::allow_ingress(const nr_transport_addr &from,
+ PortMapping **port_mapping_used) const {
+ // This is only called for traffic arriving at a port mapping
+ MOZ_ASSERT(nat_->enabled_);
+ MOZ_ASSERT(!nat_->is_an_internal_tuple(from));
+
+ *port_mapping_used = get_port_mapping(from, nat_->filtering_type_);
+ if (!(*port_mapping_used)) {
+ r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
+ "Filtered",
+ internal_socket_->my_addr().as_string,
+ from.as_string);
+ return false;
+ }
+
+ if (is_port_mapping_stale(**port_mapping_used)) {
+ r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
+ "Stale port mapping",
+ internal_socket_->my_addr().as_string,
+ from.as_string);
+ return false;
+ }
+
+ if (!nat_->allow_hairpinning_ && nat_->is_my_external_tuple(from)) {
+ r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: "
+ "Hairpinning disallowed",
+ internal_socket_->my_addr().as_string,
+ from.as_string);
+ return false;
+ }
+
+ return true;
+}
+
+int TestNrSocket::connect(nr_transport_addr *addr) {
+
+ if (connect_invoked_ || !port_mappings_.empty()) {
+ MOZ_CRASH("TestNrSocket::connect() called more than once!");
+ return R_INTERNAL;
+ }
+
+ if (!nat_->enabled_
+ || addr->protocol==IPPROTO_UDP // Horrible hack to allow default address
+ // discovery to work. Only works because
+ // we don't normally connect on UDP.
+ || nat_->is_an_internal_tuple(*addr)) {
+ // This will set connect_invoked_
+ return internal_socket_->connect(addr);
+ }
+
+ RefPtr<NrSocketBase> external_socket(create_external_socket(*addr));
+ if (!external_socket) {
+ return R_INTERNAL;
+ }
+
+ PortMapping *port_mapping = create_port_mapping(*addr, external_socket);
+ port_mappings_.push_back(port_mapping);
+ int r = port_mapping->external_socket_->connect(addr);
+ if (r && r != R_WOULDBLOCK) {
+ return r;
+ }
+
+ port_mapping->last_used_ = PR_IntervalNow();
+
+ if (poll_flags() & PR_POLL_READ) {
+ port_mapping->async_wait(NR_ASYNC_WAIT_READ,
+ port_mapping_tcp_passthrough_callback,
+ this,
+ (char*)__FUNCTION__,
+ __LINE__);
+ }
+
+ return r;
+}
+
+int TestNrSocket::write(const void *msg, size_t len, size_t *written) {
+
+ if (port_mappings_.empty()) {
+ // The no-nat case, just pass call through.
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s writing",
+ my_addr().as_string);
+
+ return internal_socket_->write(msg, len, written);
+ } else {
+ destroy_stale_port_mappings();
+ if (port_mappings_.empty()) {
+ return -1;
+ }
+ // This is TCP only
+ MOZ_ASSERT(port_mappings_.size() == 1);
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "PortMapping %s -> %s writing",
+ port_mappings_.front()->external_socket_->my_addr().as_string,
+ port_mappings_.front()->remote_address_.as_string);
+
+ port_mappings_.front()->last_used_ = PR_IntervalNow();
+ return port_mappings_.front()->external_socket_->write(msg, len, written);
+ }
+}
+
+int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) {
+
+ if (port_mappings_.empty()) {
+ return internal_socket_->read(buf, maxlen, len);
+ } else {
+ MOZ_ASSERT(port_mappings_.size() == 1);
+ int bytesRead =
+ port_mappings_.front()->external_socket_->read(buf, maxlen, len);
+ if (bytesRead > 0 && nat_->refresh_on_ingress_) {
+ port_mappings_.front()->last_used_ = PR_IntervalNow();
+ }
+ return bytesRead;
+ }
+}
+
+int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s waiting for %s",
+ internal_socket_->my_addr().as_string,
+ how == NR_ASYNC_WAIT_READ ? "read" : "write");
+
+ int r;
+
+ if (how == NR_ASYNC_WAIT_READ) {
+ NrSocketBase::async_wait(how, cb, cb_arg, function, line);
+
+ // Make sure we're waiting on the socket for the internal address
+ r = internal_socket_->async_wait(how,
+ socket_readable_callback,
+ this,
+ function,
+ line);
+ } else {
+ // For write, just use the readiness of the internal socket, since we queue
+ // everything for the port mappings.
+ r = internal_socket_->async_wait(how,
+ cb,
+ cb_arg,
+ function,
+ line);
+ }
+
+ if (r) {
+ r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for "
+ "internal socket: %d\n",
+ internal_socket_->my_addr().as_string,
+ r);
+ return r;
+ }
+
+ if (is_tcp_connection_behind_nat()) {
+ // Bypass all port-mapping related logic
+ return 0;
+ }
+
+ if (internal_socket_->my_addr().protocol == IPPROTO_TCP) {
+ // For a TCP connection through a simulated NAT, these signals are
+ // just passed through.
+ MOZ_ASSERT(port_mappings_.size() == 1);
+
+ return port_mappings_.front()->async_wait(
+ how,
+ port_mapping_tcp_passthrough_callback,
+ this,
+ function,
+ line);
+ } else if (how == NR_ASYNC_WAIT_READ) {
+ // For UDP port mappings, we decouple the writeable callbacks
+ for (PortMapping *port_mapping : port_mappings_) {
+ // Be ready to receive traffic on our port mappings
+ r = port_mapping->async_wait(how,
+ socket_readable_callback,
+ this,
+ function,
+ line);
+ if (r) {
+ r_log(LOG_GENERIC, LOG_ERR, "TestNrSocket %s failed to async_wait for "
+ "port mapping: %d\n",
+ internal_socket_->my_addr().as_string,
+ r);
+ return r;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void TestNrSocket::cancel_port_mapping_async_wait(int how) {
+ for (PortMapping *port_mapping : port_mappings_) {
+ port_mapping->cancel(how);
+ }
+}
+
+int TestNrSocket::cancel(int how) {
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s stop waiting for %s",
+ internal_socket_->my_addr().as_string,
+ how == NR_ASYNC_WAIT_READ ? "read" : "write");
+
+ // Writable callbacks are decoupled except for the TCP case
+ if (how == NR_ASYNC_WAIT_READ ||
+ internal_socket_->my_addr().protocol == IPPROTO_TCP) {
+ cancel_port_mapping_async_wait(how);
+ }
+
+ return internal_socket_->cancel(how);
+}
+
+bool TestNrSocket::has_port_mappings() const {
+ return !port_mappings_.empty();
+}
+
+bool TestNrSocket::is_my_external_tuple(const nr_transport_addr &addr) const {
+ for (PortMapping *port_mapping : port_mappings_) {
+ nr_transport_addr port_mapping_addr;
+ if (port_mapping->external_socket_->getaddr(&port_mapping_addr)) {
+ MOZ_CRASH("NrSocket::getaddr failed!");
+ }
+
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&addr),
+ &port_mapping_addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TestNrSocket::is_port_mapping_stale(
+ const PortMapping &port_mapping) const {
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime elapsed_ticks = now - port_mapping.last_used_;
+ uint32_t idle_duration = PR_IntervalToMilliseconds(elapsed_ticks);
+ return idle_duration > nat_->mapping_timeout_;
+}
+
+void TestNrSocket::destroy_stale_port_mappings() {
+ for (auto i = port_mappings_.begin(); i != port_mappings_.end();) {
+ auto temp = i;
+ ++i;
+ if (is_port_mapping_stale(**temp)) {
+ r_log(LOG_GENERIC, LOG_INFO,
+ "TestNrSocket %s destroying port mapping %s -> %s",
+ internal_socket_->my_addr().as_string,
+ (*temp)->external_socket_->my_addr().as_string,
+ (*temp)->remote_address_.as_string);
+
+ port_mappings_.erase(temp);
+ }
+ }
+}
+
+void TestNrSocket::socket_readable_callback(void *real_sock_v,
+ int how,
+ void *test_sock_v) {
+ TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
+ NrSocketBase *real_socket = static_cast<NrSocketBase*>(real_sock_v);
+
+ test_socket->on_socket_readable(real_socket);
+}
+
+void TestNrSocket::on_socket_readable(NrSocketBase *real_socket) {
+ if (!readable_socket_ && (real_socket != internal_socket_)) {
+ readable_socket_ = real_socket;
+ }
+
+ fire_readable_callback();
+}
+
+void TestNrSocket::fire_readable_callback() {
+ MOZ_ASSERT(poll_flags() & PR_POLL_READ);
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %s ready for read",
+ internal_socket_->my_addr().as_string);
+ fire_callback(NR_ASYNC_WAIT_READ);
+}
+
+void TestNrSocket::port_mapping_writeable_callback(void *ext_sock_v,
+ int how,
+ void *test_sock_v) {
+ TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
+ NrSocketBase *external_socket = static_cast<NrSocketBase*>(ext_sock_v);
+
+ test_socket->write_to_port_mapping(external_socket);
+}
+
+void TestNrSocket::write_to_port_mapping(NrSocketBase *external_socket) {
+ MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
+
+ int r = 0;
+ for (PortMapping *port_mapping : port_mappings_) {
+ if (port_mapping->external_socket_ == external_socket) {
+ // If the send succeeds, or if there was nothing to send, we keep going
+ r = port_mapping->send_from_queue();
+ if (r) {
+ break;
+ }
+ }
+ }
+
+ if (r == R_WOULDBLOCK) {
+ // Re-register for writeable callbacks, since we still have stuff to send
+ NR_ASYNC_WAIT(external_socket,
+ NR_ASYNC_WAIT_WRITE,
+ &TestNrSocket::port_mapping_writeable_callback,
+ this);
+ }
+}
+
+void TestNrSocket::port_mapping_tcp_passthrough_callback(void *ext_sock_v,
+ int how,
+ void *test_sock_v) {
+ TestNrSocket *test_socket = static_cast<TestNrSocket*>(test_sock_v);
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %s firing %s callback",
+ test_socket->internal_socket_->my_addr().as_string,
+ how == NR_ASYNC_WAIT_READ ? "readable" : "writeable");
+
+
+ test_socket->internal_socket_->fire_callback(how);
+}
+
+bool TestNrSocket::is_tcp_connection_behind_nat() const {
+ return internal_socket_->my_addr().protocol == IPPROTO_TCP &&
+ port_mappings_.empty();
+}
+
+TestNrSocket::PortMapping* TestNrSocket::get_port_mapping(
+ const nr_transport_addr &remote_address,
+ TestNat::NatBehavior filter) const {
+ int compare_flags;
+ switch (filter) {
+ case TestNat::ENDPOINT_INDEPENDENT:
+ compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL;
+ break;
+ case TestNat::ADDRESS_DEPENDENT:
+ compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ADDR;
+ break;
+ case TestNat::PORT_DEPENDENT:
+ compare_flags = NR_TRANSPORT_ADDR_CMP_MODE_ALL;
+ break;
+ }
+
+ for (PortMapping *port_mapping : port_mappings_) {
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ if (!nr_transport_addr_cmp(const_cast<nr_transport_addr*>(&remote_address),
+ &port_mapping->remote_address_,
+ compare_flags))
+ return port_mapping;
+ }
+ return nullptr;
+}
+
+TestNrSocket::PortMapping* TestNrSocket::create_port_mapping(
+ const nr_transport_addr &remote_address,
+ const RefPtr<NrSocketBase> &external_socket) const {
+ r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s creating port mapping %s -> %s",
+ internal_socket_->my_addr().as_string,
+ external_socket->my_addr().as_string,
+ remote_address.as_string);
+
+ return new PortMapping(remote_address, external_socket);
+}
+
+TestNrSocket::PortMapping::PortMapping(
+ const nr_transport_addr &remote_address,
+ const RefPtr<NrSocketBase> &external_socket) :
+ external_socket_(external_socket) {
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ nr_transport_addr_copy(&remote_address_,
+ const_cast<nr_transport_addr*>(&remote_address));
+}
+
+int TestNrSocket::PortMapping::send_from_queue() {
+ MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
+ int r = 0;
+
+ while (!send_queue_.empty()) {
+ UdpPacket &packet = *send_queue_.front();
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "PortMapping %s -> %s sending from queue to %s",
+ external_socket_->my_addr().as_string,
+ remote_address_.as_string,
+ packet.remote_address_.as_string);
+
+ r = external_socket_->sendto(packet.buffer_->data(),
+ packet.buffer_->len(),
+ 0,
+ &packet.remote_address_);
+
+ if (r) {
+ if (r != R_WOULDBLOCK) {
+ r_log(LOG_GENERIC, LOG_ERR, "%s: Fatal error %d, stop trying",
+ __FUNCTION__, r);
+ send_queue_.clear();
+ } else {
+ r_log(LOG_GENERIC, LOG_DEBUG, "Would block, will retry later");
+ }
+ break;
+ }
+
+ send_queue_.pop_front();
+ }
+
+ return r;
+}
+
+int TestNrSocket::PortMapping::sendto(const void *msg,
+ size_t len,
+ const nr_transport_addr &to) {
+ MOZ_ASSERT(remote_address_.protocol != IPPROTO_TCP);
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "PortMapping %s -> %s sending to %s",
+ external_socket_->my_addr().as_string,
+ remote_address_.as_string,
+ to.as_string);
+
+ last_used_ = PR_IntervalNow();
+ int r = external_socket_->sendto(msg, len, 0,
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ const_cast<nr_transport_addr*>(&to));
+
+ if (r == R_WOULDBLOCK) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "Enqueueing UDP packet to %s", to.as_string);
+ send_queue_.push_back(RefPtr<UdpPacket>(new UdpPacket(msg, len, to)));
+ return 0;
+ } else if (r) {
+ r_log(LOG_GENERIC,LOG_ERR, "Error: %d", r);
+ }
+
+ return r;
+}
+
+int TestNrSocket::PortMapping::async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line) {
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "PortMapping %s -> %s waiting for %s",
+ external_socket_->my_addr().as_string,
+ remote_address_.as_string,
+ how == NR_ASYNC_WAIT_READ ? "read" : "write");
+
+ return external_socket_->async_wait(how, cb, cb_arg, function, line);
+}
+
+int TestNrSocket::PortMapping::cancel(int how) {
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "PortMapping %s -> %s stop waiting for %s",
+ external_socket_->my_addr().as_string,
+ remote_address_.as_string,
+ how == NR_ASYNC_WAIT_READ ? "read" : "write");
+
+ return external_socket_->cancel(how);
+}
+
+} // namespace mozilla
+
diff --git a/media/mtransport/test_nr_socket.h b/media/mtransport/test_nr_socket.h
new file mode 100644
index 000000000..a38e2e5ed
--- /dev/null
+++ b/media/mtransport/test_nr_socket.h
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+/*
+*/
+
+/*
+Based partially on original code from nICEr and nrappkit.
+
+nICEr copyright:
+
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+nrappkit copyright:
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Thu Dec 20 20:14:49 2001
+*/
+
+// Original author: bcampen@mozilla.com [:bwc]
+
+#ifndef test_nr_socket__
+#define test_nr_socket__
+
+extern "C" {
+#include "transport_addr.h"
+}
+
+#include "nr_socket_prsock.h"
+
+extern "C" {
+#include "nr_socket.h"
+}
+
+#include <set>
+#include <vector>
+#include <map>
+#include <list>
+#include <string>
+
+#include "mozilla/UniquePtr.h"
+#include "prinrval.h"
+
+namespace mozilla {
+
+class TestNrSocket;
+
+/**
+ * A group of TestNrSockets that behave as if they were behind the same NAT.
+ * @note We deliberately avoid addref/release of TestNrSocket here to avoid
+ * masking lifetime errors elsewhere.
+*/
+class TestNat {
+ public:
+ typedef enum {
+ /** For mapping, one port is used for all destinations.
+ * For filtering, allow any external address/port. */
+ ENDPOINT_INDEPENDENT,
+
+ /** For mapping, one port for each destination address (for any port).
+ * For filtering, allow incoming traffic from addresses that outgoing
+ * traffic has been sent to. */
+ ADDRESS_DEPENDENT,
+
+ /** For mapping, one port for each destination address/port.
+ * For filtering, allow incoming traffic only from addresses/ports that
+ * outgoing traffic has been sent to. */
+ PORT_DEPENDENT,
+ } NatBehavior;
+
+ TestNat() :
+ enabled_(false),
+ filtering_type_(ENDPOINT_INDEPENDENT),
+ mapping_type_(ENDPOINT_INDEPENDENT),
+ mapping_timeout_(30000),
+ allow_hairpinning_(false),
+ refresh_on_ingress_(false),
+ block_udp_(false),
+ block_stun_(false),
+ delay_stun_resp_ms_(0),
+ sockets_() {}
+
+ bool has_port_mappings() const;
+
+ // Helps determine whether we're hairpinning
+ bool is_my_external_tuple(const nr_transport_addr &addr) const;
+ bool is_an_internal_tuple(const nr_transport_addr &addr) const;
+
+ int create_socket_factory(nr_socket_factory **factorypp);
+
+ void insert_socket(TestNrSocket *socket) {
+ sockets_.insert(socket);
+ }
+
+ void erase_socket(TestNrSocket *socket) {
+ sockets_.erase(socket);
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNat);
+
+ static NatBehavior ToNatBehavior(const std::string& type);
+
+ bool enabled_;
+ TestNat::NatBehavior filtering_type_;
+ TestNat::NatBehavior mapping_type_;
+ uint32_t mapping_timeout_;
+ bool allow_hairpinning_;
+ bool refresh_on_ingress_;
+ bool block_udp_;
+ bool block_stun_;
+ /* Note: this can only delay a single response so far (bug 1253657) */
+ uint32_t delay_stun_resp_ms_;
+
+ private:
+ std::set<TestNrSocket*> sockets_;
+
+ ~TestNat(){}
+};
+
+/**
+ * Subclass of NrSocketBase that can simulate things like being behind a NAT,
+ * packet loss, latency, packet rewriting, etc. Also exposes some stuff that
+ * assists in diagnostics.
+ * This is accomplished by wrapping an "internal" socket (that handles traffic
+ * behind the NAT), and a collection of "external" sockets (that handle traffic
+ * into/out of the NAT)
+ */
+class TestNrSocket : public NrSocketBase {
+ public:
+ explicit TestNrSocket(TestNat *nat);
+
+ bool has_port_mappings() const;
+ bool is_my_external_tuple(const nr_transport_addr &addr) const;
+
+ // Overrides of NrSocketBase
+ int create(nr_transport_addr *addr) override;
+ int sendto(const void *msg, size_t len,
+ int flags, nr_transport_addr *to) override;
+ int recvfrom(void * buf, size_t maxlen,
+ size_t *len, int flags,
+ nr_transport_addr *from) override;
+ int getaddr(nr_transport_addr *addrp) override;
+ void close() override;
+ int connect(nr_transport_addr *addr) override;
+ int write(const void *msg, size_t len, size_t *written) override;
+ int read(void *buf, size_t maxlen, size_t *len) override;
+
+ int listen(int backlog) override;
+ int accept(nr_transport_addr *addrp, nr_socket **sockp) override;
+ int async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line) override;
+ int cancel(int how) override;
+
+ // Need override since this is virtual in NrSocketBase
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestNrSocket, override)
+
+ private:
+ virtual ~TestNrSocket();
+
+ class UdpPacket {
+ public:
+ UdpPacket(const void *msg, size_t len, const nr_transport_addr &addr) :
+ buffer_(new DataBuffer(static_cast<const uint8_t*>(msg), len)) {
+ // TODO(bug 1170299): Remove const_cast when no longer necessary
+ nr_transport_addr_copy(&remote_address_,
+ const_cast<nr_transport_addr*>(&addr));
+ }
+
+ nr_transport_addr remote_address_;
+ UniquePtr<DataBuffer> buffer_;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UdpPacket);
+ private:
+ ~UdpPacket(){}
+ };
+
+ class PortMapping {
+ public:
+ PortMapping(const nr_transport_addr &remote_address,
+ const RefPtr<NrSocketBase> &external_socket);
+
+ int sendto(const void *msg, size_t len, const nr_transport_addr &to);
+ int async_wait(int how, NR_async_cb cb, void *cb_arg,
+ char *function, int line);
+ int cancel(int how);
+ int send_from_queue();
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PortMapping);
+
+ PRIntervalTime last_used_;
+ RefPtr<NrSocketBase> external_socket_;
+ // For non-symmetric, most of the data here doesn't matter
+ nr_transport_addr remote_address_;
+
+ private:
+ ~PortMapping() {
+ external_socket_->close();
+ }
+
+ // If external_socket_ returns E_WOULDBLOCK, we don't want to propagate
+ // that to the code using the TestNrSocket. We can also perhaps use this
+ // to help simulate things like latency.
+ std::list<RefPtr<UdpPacket>> send_queue_;
+ };
+
+ struct DeferredPacket {
+ DeferredPacket(TestNrSocket *sock,
+ const void *data, size_t len,
+ int flags,
+ nr_transport_addr *addr,
+ RefPtr<NrSocketBase> internal_socket) :
+ socket_(sock),
+ buffer_(reinterpret_cast<const uint8_t *>(data), len),
+ flags_(flags),
+ internal_socket_(internal_socket) {
+ nr_transport_addr_copy(&to_, addr);
+ }
+
+ TestNrSocket *socket_;
+ DataBuffer buffer_;
+ int flags_;
+ nr_transport_addr to_;
+ RefPtr<NrSocketBase> internal_socket_;
+ };
+
+ bool is_port_mapping_stale(const PortMapping &port_mapping) const;
+ bool allow_ingress(const nr_transport_addr &from,
+ PortMapping **port_mapping_used) const;
+ void destroy_stale_port_mappings();
+
+ static void socket_readable_callback(void *real_sock_v,
+ int how,
+ void *test_sock_v);
+ void on_socket_readable(NrSocketBase *external_or_internal_socket);
+ void fire_readable_callback();
+
+ static void port_mapping_tcp_passthrough_callback(void *ext_sock_v,
+ int how,
+ void *test_sock_v);
+ void cancel_port_mapping_async_wait(int how);
+
+ static void port_mapping_writeable_callback(void *ext_sock_v,
+ int how,
+ void *test_sock_v);
+ void write_to_port_mapping(NrSocketBase *external_socket);
+ bool is_tcp_connection_behind_nat() const;
+
+ PortMapping* get_port_mapping(const nr_transport_addr &remote_addr,
+ TestNat::NatBehavior filter) const;
+ PortMapping* create_port_mapping(
+ const nr_transport_addr &remote_addr,
+ const RefPtr<NrSocketBase> &external_socket) const;
+ RefPtr<NrSocketBase> create_external_socket(
+ const nr_transport_addr &remote_addr) const;
+
+ static void process_delayed_cb(NR_SOCKET s, int how, void *cb_arg);
+
+ RefPtr<NrSocketBase> readable_socket_;
+ // The socket for the "internal" address; used to talk to stuff behind the
+ // same nat.
+ RefPtr<NrSocketBase> internal_socket_;
+ RefPtr<TestNat> nat_;
+ // Since our comparison logic is different depending on what kind of NAT
+ // we simulate, and the STL does not make it very easy to switch out the
+ // comparison function at runtime, and these lists are going to be very
+ // small anyway, we just brute-force it.
+ std::list<RefPtr<PortMapping>> port_mappings_;
+
+ void *timer_handle_;
+};
+
+} // namespace mozilla
+
+#endif // test_nr_socket__
+
diff --git a/media/mtransport/testlib/moz.build b/media/mtransport/testlib/moz.build
new file mode 100644
index 000000000..fc11bb311
--- /dev/null
+++ b/media/mtransport/testlib/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include('../common.build')
+include("/ipc/chromium/chromium-config.mozbuild")
+
+# These files cannot be built in unified mode because of the redefinition of
+# getLogModule, UNIMPLEMENTED, nr_socket_long_term_violation_time,
+# nr_socket_short_term_violation_time.
+SOURCES += mtransport_cppsrcs
+
+Library('mtransport_s')
+
+FORCE_STATIC_LIB = True
diff --git a/media/mtransport/third_party/import.py b/media/mtransport/third_party/import.py
new file mode 100644
index 000000000..6defdf26b
--- /dev/null
+++ b/media/mtransport/third_party/import.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# Execute as: /import.py <source-distribution> <target-dir>
+# <target-dir> must have 'IMPORT_FILES' in it
+
+import sys
+import re
+import os
+import shutil
+
+def die(msg):
+ sys.stderr.write('ERROR:' + msg + '\n')
+ sys.exit(1)
+
+DISTRO = sys.argv[1]
+IMPORT_DIR = sys.argv[2]
+FILES = []
+
+f = open("%s/IMPORT_FILES" % IMPORT_DIR)
+for l in f:
+ l = l.strip()
+ l = l.strip("'")
+ if l.startswith("#"):
+ continue
+ if not l:
+ continue
+ FILES.append(l)
+
+for f in FILES:
+ print f
+ SOURCE_PATH = "%s/%s"%(DISTRO,f)
+ DEST_PATH = "%s/%s"%(IMPORT_DIR,f)
+ if not os.path.exists(SOURCE_PATH):
+ die("%s does not exist"%SOURCE_PATH)
+ if not os.path.exists(os.path.dirname(DEST_PATH)):
+ os.makedirs(os.path.dirname(DEST_PATH))
+ shutil.copyfile(SOURCE_PATH, DEST_PATH)
diff --git a/media/mtransport/third_party/moz.build b/media/mtransport/third_party/moz.build
new file mode 100644
index 000000000..4d4013819
--- /dev/null
+++ b/media/mtransport/third_party/moz.build
@@ -0,0 +1,76 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include('/build/gyp.mozbuild')
+
+GYP_DIRS += [
+ 'nICEr',
+ 'nrappkit',
+]
+
+# These files cannot be built in unified mode because of name clashes on RCSSTRING
+nICEr_non_unified_sources = [
+ 'nICEr/src/crypto/nr_crypto.c',
+ 'nICEr/src/ice/ice_candidate.c',
+ 'nICEr/src/ice/ice_candidate_pair.c',
+ 'nICEr/src/ice/ice_component.c',
+ 'nICEr/src/ice/ice_ctx.c',
+ 'nICEr/src/ice/ice_media_stream.c',
+ 'nICEr/src/ice/ice_parser.c',
+ 'nICEr/src/ice/ice_peer_ctx.c',
+ 'nICEr/src/ice/ice_socket.c',
+ 'nICEr/src/net/nr_socket.c',
+ 'nICEr/src/net/transport_addr.c',
+ 'nICEr/src/net/transport_addr_reg.c',
+ 'nICEr/src/stun/addrs.c',
+ 'nICEr/src/stun/nr_socket_turn.c',
+ 'nICEr/src/stun/stun_build.c',
+ 'nICEr/src/stun/stun_client_ctx.c',
+ 'nICEr/src/stun/stun_codec.c',
+ 'nICEr/src/stun/stun_hint.c',
+ 'nICEr/src/stun/stun_msg.c',
+ 'nICEr/src/stun/stun_proc.c',
+ 'nICEr/src/stun/stun_server_ctx.c',
+ 'nICEr/src/stun/stun_util.c',
+ 'nICEr/src/stun/turn_client_ctx.c',
+ 'nICEr/src/util/cb_args.c',
+ 'nICEr/src/util/ice_util.c',
+]
+
+# This file cannot be built in unified mode because it breaks Linux ASAN builds
+nICEr_non_unified_sources += [
+ 'nICEr/src/util/mbslen.c',
+]
+nrappkit_non_unified_sources = [
+ 'nrappkit/src/log/r_log.c',
+ 'nrappkit/src/util/byteorder.c',
+ 'nrappkit/src/util/hex.c',
+ 'nrappkit/src/util/libekr/debug.c',
+ 'nrappkit/src/util/libekr/r_assoc.c',
+ 'nrappkit/src/util/libekr/r_crc32.c',
+ 'nrappkit/src/util/libekr/r_data.c',
+ 'nrappkit/src/util/libekr/r_errors.c',
+ 'nrappkit/src/util/libekr/r_list.c',
+ 'nrappkit/src/util/libekr/r_memory.c',
+ 'nrappkit/src/util/libekr/r_replace.c',
+ 'nrappkit/src/util/libekr/r_time.c',
+ 'nrappkit/src/util/p_buf.c',
+ 'nrappkit/src/util/util.c',
+]
+
+GYP_DIRS['nICEr'].input = 'nICEr/nicer.gyp'
+GYP_DIRS['nICEr'].variables = gyp_vars
+# We allow warnings for third-party code that can be updated from upstream.
+GYP_DIRS['nICEr'].sandbox_vars['ALLOW_COMPILER_WARNINGS'] = True
+GYP_DIRS['nICEr'].sandbox_vars['FINAL_LIBRARY'] = 'webrtc'
+GYP_DIRS['nICEr'].non_unified_sources += nICEr_non_unified_sources
+
+GYP_DIRS['nrappkit'].input = 'nrappkit/nrappkit.gyp'
+GYP_DIRS['nrappkit'].variables = gyp_vars
+# We allow warnings for third-party code that can be updated from upstream.
+GYP_DIRS['nrappkit'].sandbox_vars['ALLOW_COMPILER_WARNINGS'] = True
+GYP_DIRS['nrappkit'].sandbox_vars['FINAL_LIBRARY'] = 'webrtc'
+GYP_DIRS['nrappkit'].non_unified_sources += nrappkit_non_unified_sources
diff --git a/media/mtransport/third_party/nICEr/IMPORT_FILES b/media/mtransport/third_party/nICEr/IMPORT_FILES
new file mode 100644
index 000000000..4b30b2141
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/IMPORT_FILES
@@ -0,0 +1,71 @@
+ # Crypto
+ ./src/crypto/nr_crypto.c
+ ./src/crypto/nr_crypto.h
+ #./src/crypto/nr_crypto_openssl.c
+ #./src/crypto/nr_crypto_openssl.h
+
+ # ICE
+ ./src/ice/ice_candidate.c
+ ./src/ice/ice_candidate.h
+ ./src/ice/ice_candidate_pair.c
+ ./src/ice/ice_candidate_pair.h
+ ./src/ice/ice_codeword.h
+ ./src/ice/ice_component.c
+ ./src/ice/ice_component.h
+ ./src/ice/ice_ctx.c
+ ./src/ice/ice_ctx.h
+ ./src/ice/ice_handler.h
+ ./src/ice/ice_media_stream.c
+ ./src/ice/ice_media_stream.h
+ ./src/ice/ice_parser.c
+ ./src/ice/ice_peer_ctx.c
+ ./src/ice/ice_peer_ctx.h
+ ./src/ice/ice_reg.h
+ ./src/ice/ice_socket.c
+ ./src/ice/ice_socket.h
+
+ # Net
+ ./src/net/nr_socket.c
+ ./src/net/nr_socket.h
+ #./src/net/nr_socket_local.c
+ ./src/net/nr_socket_local.h
+ ./src/net/transport_addr.c
+ ./src/net/transport_addr.h
+ ./src/net/transport_addr_reg.c
+ ./src/net/transport_addr_reg.h
+
+ # STUN
+ ./src/stun/addrs.c
+ ./src/stun/addrs.h
+ ./src/stun/ifaddrs-android.c
+ ./src/stun/ifaddrs-android.h
+ ./src/stun/nr_socket_turn.c
+ ./src/stun/nr_socket_turn.h
+ ./src/stun/stun.h
+ ./src/stun/stun_build.c
+ ./src/stun/stun_build.h
+ ./src/stun/stun_client_ctx.c
+ ./src/stun/stun_client_ctx.h
+ ./src/stun/stun_codec.c
+ ./src/stun/stun_codec.h
+ ./src/stun/stun_hint.c
+ ./src/stun/stun_hint.h
+ ./src/stun/stun_msg.c
+ ./src/stun/stun_msg.h
+ ./src/stun/stun_proc.c
+ ./src/stun/stun_proc.h
+ ./src/stun/stun_reg.h
+ ./src/stun/stun_server_ctx.c
+ ./src/stun/stun_server_ctx.h
+ ./src/stun/stun_util.c
+ ./src/stun/stun_util.h
+ ./src/stun/turn_client_ctx.c
+ ./src/stun/turn_client_ctx.h
+
+ # Util
+ ./src/util/cb_args.c
+ ./src/util/cb_args.h
+ ./src/util/ice_util.c
+ ./src/util/ice_util.h
+ ./src/util/mbslen.c
+ ./src/util/mbslen.h
diff --git a/media/mtransport/third_party/nICEr/README b/media/mtransport/third_party/nICEr/README
new file mode 100644
index 000000000..390203dec
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/README
@@ -0,0 +1,74 @@
+ nICEr 1.0
+
+PREREQUISITES:
+-------------
+You must first obtain and build the following packages:
+
+* nrappkit
+ - http://nrappkit.sourceforge.net/
+
+* OpenSSL-0.9.8g
+ - http://www.openssl.org/source/openssl-0.9.8g.tar.gz
+
+
+For best results, the "ice-<version>" directory should be at
+the same level as the "nrappkit" and "openssl-0.9.8g"
+directories. I.e., the directory structure should look like:
+
+ nrappkit/
+ ice-<version>/
+ openssl/
+ include/
+ lib/VC/
+
+
+BUILDING ON UNIX:
+----------------
+Once the prerequisite packages are built, 'cd' to the
+relevant build directory, one of:
+
+ ice-<version>/make/darwin
+ ice-<version>/make/linux-fedora
+ ice-<version>/make/ubuntu
+
+and simply do a "make".
+
+
+BUILDING ON WINDOWS:
+-------------------
+The Visual C++ project files are configured to expect the
+directory structure described above.
+
+Note: Binary Windows builds of OpenSSL can be found at:
+ http://www.slproweb.com/products/Win32OpenSSL.html
+
+Once the prerequisite packages are built, open the VC++ 9.0
+solution file: ICE/make/win32/ice.sln and build the solution.
+Note: Since the VC++ project/solution files are version 9.0,
+Visual Studio 2008 is required.
+
+
+STATUS:
+------
+The ICE code has been tested on the following platforms:
+-- Fedora Core 4 (Intel 32-bit)
+-- Fedora Core 6 (Intel 32-bit)
+-- Ubuntu 6.10
+-- MacOSX 10.4.9
+-- Windows Vista (Home Premium)
+-- Windows XP Pro
+-- Windows 2000 SP4
+
+
+KNOWN ISSUES:
+------------
+-- TURN SET-ACTIVE-DESTINATION mode not yet supported.
+
+-- Problems may exist with the TURN client implementation; the TURN code
+ has received minimal testing due to the unavailability of a real
+ TURN server to test against.
+
+-- The ICE-Lite implementation is not complete.
+
+-- The new "impatient" timeout has not yet been thoroughly tested.
+
diff --git a/media/mtransport/third_party/nICEr/README_MOZILLA b/media/mtransport/third_party/nICEr/README_MOZILLA
new file mode 100644
index 000000000..8e058ac7d
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/README_MOZILLA
@@ -0,0 +1,11 @@
+This code was imported from:
+
+https://svn.resiprocate.org/rep/resiprocate/main/nICEr
+
+SVN revision 9873
+
+Some minor changes have been made to improve portability as well
+as to add trickle ICE. These changes are being submitted for
+upstreaming and can be found in upstream.diff.
+
+
diff --git a/media/mtransport/third_party/nICEr/nicer.gyp b/media/mtransport/third_party/nICEr/nicer.gyp
new file mode 100644
index 000000000..9df4a4b28
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/nicer.gyp
@@ -0,0 +1,254 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# nrappkit.gyp
+#
+#
+{
+ 'variables' : {
+ 'build_with_gonk%': 0,
+ 'have_ethtool_cmd_speed_hi%': 1
+ },
+ 'targets' : [
+ {
+ 'target_name' : 'nicer',
+ 'type' : 'static_library',
+
+ 'include_dirs' : [
+ ## EXTERNAL
+ # nrappkit
+ '../nrappkit/src/event',
+ '../nrappkit/src/log',
+ '../nrappkit/src/plugin',
+ '../nrappkit/src/registry',
+ '../nrappkit/src/share',
+ '../nrappkit/src/stats',
+ '../nrappkit/src/util',
+ '../nrappkit/src/util/libekr',
+ '../nrappkit/src/port/generic/include',
+
+ # INTERNAL
+ "./src/crypto",
+ "./src/ice",
+ "./src/net",
+ "./src/stun",
+ "./src/util",
+ ],
+
+ 'sources' : [
+ # Crypto
+ "./src/crypto/nr_crypto.c",
+ "./src/crypto/nr_crypto.h",
+ #"./src/crypto/nr_crypto_openssl.c",
+ #"./src/crypto/nr_crypto_openssl.h",
+
+ # ICE
+ "./src/ice/ice_candidate.c",
+ "./src/ice/ice_candidate.h",
+ "./src/ice/ice_candidate_pair.c",
+ "./src/ice/ice_candidate_pair.h",
+ "./src/ice/ice_codeword.h",
+ "./src/ice/ice_component.c",
+ "./src/ice/ice_component.h",
+ "./src/ice/ice_ctx.c",
+ "./src/ice/ice_ctx.h",
+ "./src/ice/ice_handler.h",
+ "./src/ice/ice_media_stream.c",
+ "./src/ice/ice_media_stream.h",
+ "./src/ice/ice_parser.c",
+ "./src/ice/ice_peer_ctx.c",
+ "./src/ice/ice_peer_ctx.h",
+ "./src/ice/ice_reg.h",
+ "./src/ice/ice_socket.c",
+ "./src/ice/ice_socket.h",
+
+ # Net
+ "./src/net/nr_resolver.c",
+ "./src/net/nr_resolver.h",
+ "./src/net/nr_socket_wrapper.c",
+ "./src/net/nr_socket_wrapper.h",
+ "./src/net/nr_proxy_tunnel.c",
+ "./src/net/nr_proxy_tunnel.h",
+ "./src/net/nr_socket.c",
+ "./src/net/nr_socket.h",
+ #"./src/net/nr_socket_local.c",
+ "./src/net/nr_socket_local.h",
+ "./src/net/nr_socket_multi_tcp.c",
+ "./src/net/nr_socket_multi_tcp.h",
+ "./src/net/transport_addr.c",
+ "./src/net/transport_addr.h",
+ "./src/net/transport_addr_reg.c",
+ "./src/net/transport_addr_reg.h",
+ "./src/net/local_addr.c",
+ "./src/net/local_addr.h",
+ "./src/net/nr_interface_prioritizer.c",
+ "./src/net/nr_interface_prioritizer.h",
+
+ # STUN
+ "./src/stun/addrs.c",
+ "./src/stun/addrs.h",
+ "./src/stun/ifaddrs-android.c",
+ "./src/stun/ifaddrs-android.h",
+ "./src/stun/nr_socket_turn.c",
+ "./src/stun/nr_socket_turn.h",
+ "./src/stun/nr_socket_buffered_stun.c",
+ "./src/stun/nr_socket_buffered_stun.h",
+ "./src/stun/stun.h",
+ "./src/stun/stun_build.c",
+ "./src/stun/stun_build.h",
+ "./src/stun/stun_client_ctx.c",
+ "./src/stun/stun_client_ctx.h",
+ "./src/stun/stun_codec.c",
+ "./src/stun/stun_codec.h",
+ "./src/stun/stun_hint.c",
+ "./src/stun/stun_hint.h",
+ "./src/stun/stun_msg.c",
+ "./src/stun/stun_msg.h",
+ "./src/stun/stun_proc.c",
+ "./src/stun/stun_proc.h",
+ "./src/stun/stun_reg.h",
+ "./src/stun/stun_server_ctx.c",
+ "./src/stun/stun_server_ctx.h",
+ "./src/stun/stun_util.c",
+ "./src/stun/stun_util.h",
+ "./src/stun/turn_client_ctx.c",
+ "./src/stun/turn_client_ctx.h",
+
+ # Util
+ "./src/util/cb_args.c",
+ "./src/util/cb_args.h",
+ "./src/util/ice_util.c",
+ "./src/util/ice_util.h",
+ "./src/util/mbslen.c",
+ "./src/util/mbslen.h",
+
+
+ ],
+
+ 'defines' : [
+ 'SANITY_CHECKS',
+ 'USE_TURN',
+ 'USE_ICE',
+ 'USE_RFC_3489_BACKWARDS_COMPATIBLE',
+ 'USE_STUND_0_96',
+ 'USE_STUN_PEDANTIC',
+ 'USE_TURN',
+ 'NR_SOCKET_IS_VOID_PTR',
+ 'restrict=',
+ 'R_PLATFORM_INT_TYPES=<stdint.h>',
+ 'R_DEFINED_INT2=int16_t',
+ 'R_DEFINED_UINT2=uint16_t',
+ 'R_DEFINED_INT4=int32_t',
+ 'R_DEFINED_UINT4=uint32_t',
+ 'R_DEFINED_INT8=int64_t',
+ 'R_DEFINED_UINT8=uint64_t',
+ ],
+
+ 'conditions' : [
+ ## Mac and BSDs
+ [ 'OS == "mac" or OS == "ios"', {
+ 'defines' : [
+ 'DARWIN',
+ ],
+ }],
+ [ 'os_bsd == 1', {
+ 'defines' : [
+ 'BSD',
+ ],
+ }],
+ [ 'OS == "mac" or OS == "ios" or os_bsd == 1', {
+ 'cflags_mozilla': [
+ '-Wall',
+ '-Wno-parentheses',
+ '-Wno-strict-prototypes',
+ '-Wmissing-prototypes',
+ ],
+ 'defines' : [
+ 'HAVE_LIBM=1',
+ 'HAVE_STRDUP=1',
+ 'HAVE_STRLCPY=1',
+ 'HAVE_SYS_TIME_H=1',
+ 'HAVE_VFPRINTF=1',
+ 'NEW_STDIO'
+ 'RETSIGTYPE=void',
+ 'TIME_WITH_SYS_TIME_H=1',
+ '__UNUSED__=__attribute__((unused))',
+ ],
+
+ 'include_dirs': [
+ '../nrappkit/src/port/darwin/include'
+ ],
+
+ 'sources': [
+ ],
+ }],
+
+ ## Win
+ [ 'OS == "win"', {
+ 'defines' : [
+ 'WIN32',
+ 'USE_ICE',
+ 'USE_TURN',
+ 'USE_RFC_3489_BACKWARDS_COMPATIBLE',
+ 'USE_STUND_0_96',
+ 'USE_STUN_PEDANTIC',
+ '_CRT_SECURE_NO_WARNINGS',
+ '__UNUSED__=',
+ 'HAVE_STRDUP',
+ 'NO_REG_RPC'
+ ],
+
+ 'include_dirs': [
+ '../nrappkit/src/port/win32/include'
+ ],
+ }],
+ ## Linux/Android
+ [ '(OS == "linux") or (OS=="android")', {
+ 'cflags_mozilla': [
+ '-Wall',
+ '-Wno-parentheses',
+ '-Wno-strict-prototypes',
+ '-Wmissing-prototypes',
+ ],
+ 'defines' : [
+ 'LINUX',
+ 'HAVE_LIBM=1',
+ 'HAVE_STRDUP=1',
+ 'HAVE_STRLCPY=1',
+ 'HAVE_SYS_TIME_H=1',
+ 'HAVE_VFPRINTF=1',
+ 'NEW_STDIO'
+ 'RETSIGTYPE=void',
+ 'TIME_WITH_SYS_TIME_H=1',
+ '__UNUSED__=__attribute__((unused))',
+ ],
+
+ 'include_dirs': [
+ '../nrappkit/src/port/linux/include'
+ ],
+
+ 'sources': [
+ ],
+ }],
+ ['moz_widget_toolkit_gonk==1', {
+ 'defines' : [
+ 'WEBRTC_GONK',
+ 'NO_REG_RPC',
+ ],
+ }],
+ # Gonk has its own nr_stun_get_addrs implementation.
+ ['build_with_gonk==1', {
+ 'defines': [
+ "USE_PLATFORM_NR_STUN_GET_ADDRS",
+ ]
+ }],
+ ['have_ethtool_cmd_speed_hi==0', {
+ 'defines': [
+ "DONT_HAVE_ETHTOOL_SPEED_HI",
+ ]
+ }]
+ ],
+ }]
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c
new file mode 100644
index 000000000..088ebf045
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c
@@ -0,0 +1,71 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: nr_crypto.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <nr_api.h>
+#include "nr_crypto.h"
+
+static int nr_ice_crypto_dummy_random_bytes(UCHAR *buf, int len)
+ {
+ fprintf(stderr,"Need to define crypto API implementation\n");
+
+ exit(1);
+ }
+
+static int nr_ice_crypto_dummy_hmac_sha1(UCHAR *key, int key_l, UCHAR *buf, int buf_l, UCHAR digest[20])
+ {
+ fprintf(stderr,"Need to define crypto API implementation\n");
+
+ exit(1);
+ }
+
+static int nr_ice_crypto_dummy_md5(UCHAR *buf, int buf_l, UCHAR digest[16])
+ {
+ fprintf(stderr,"Need to define crypto API implementation\n");
+
+ exit(1);
+ }
+
+static nr_ice_crypto_vtbl nr_ice_crypto_dummy_vtbl= {
+ nr_ice_crypto_dummy_random_bytes,
+ nr_ice_crypto_dummy_hmac_sha1,
+ nr_ice_crypto_dummy_md5
+};
+
+
+
+nr_ice_crypto_vtbl *nr_crypto_vtbl=&nr_ice_crypto_dummy_vtbl;
+
+
diff --git a/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h
new file mode 100644
index 000000000..4c71786f0
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h
@@ -0,0 +1,52 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _nr_crypto_h
+#define _nr_crypto_h
+
+
+typedef struct nr_ice_crypto_vtbl_ {
+ int (*random_bytes)(UCHAR *buf, int len);
+ int (*hmac_sha1)(UCHAR *key, int key_l, UCHAR *buf, int buf_l, UCHAR digest[20]);
+ int (*md5)(UCHAR *buf, int buf_l, UCHAR digest[16]);
+} nr_ice_crypto_vtbl;
+
+extern nr_ice_crypto_vtbl *nr_crypto_vtbl;
+
+#define nr_crypto_random_bytes(a,b) nr_crypto_vtbl->random_bytes(a,b)
+#define nr_crypto_hmac_sha1(a,b,c,d,e) nr_crypto_vtbl->hmac_sha1(a,b,c,d,e)
+#define nr_crypto_md5(a,b,c) nr_crypto_vtbl->md5(a,b,c)
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
new file mode 100644
index 000000000..e48b7b5e1
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c
@@ -0,0 +1,979 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_candidate.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <csi_platform.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include "nr_api.h"
+#include "registry.h"
+#include "nr_socket.h"
+#include "async_timer.h"
+
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "turn_client_ctx.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_codeword.h"
+#include "ice_reg.h"
+#include "ice_util.h"
+#include "nr_socket_turn.h"
+#include "nr_socket.h"
+#include "nr_socket_multi_tcp.h"
+
+static int next_automatic_preference = 127;
+
+static int nr_ice_candidate_initialize2(nr_ice_candidate *cand);
+static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand);
+static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand);
+static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg);
+#ifdef USE_TURN
+static int nr_ice_start_relay_turn(nr_ice_candidate *cand);
+static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg);
+static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr);
+#endif /* USE_TURN */
+
+void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand)
+ {
+ char as_string[1024];
+
+ snprintf(as_string,
+ sizeof(as_string),
+ "%s(%s)",
+ cand->addr.as_string,
+ cand->label);
+
+ nr_ice_compute_codeword(as_string,strlen(as_string),cand->codeword);
+ }
+
+char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0};
+char *nr_ice_candidate_tcp_type_names[]={0,"active","passive","so",0};
+
+static const char *nr_ctype_name(nr_ice_candidate_type ctype) {
+ assert(ctype<CTYPE_MAX && ctype>0);
+ if (ctype <= 0 || ctype >= CTYPE_MAX) {
+ return "ERROR";
+ }
+ return nr_ice_candidate_type_names[ctype];
+}
+
+static const char *nr_tcp_type_name(nr_socket_tcp_type tcp_type) {
+ assert(tcp_type<TCP_TYPE_MAX && tcp_type>0);
+ if (tcp_type <= 0 || tcp_type >= TCP_TYPE_MAX) {
+ return "ERROR";
+ }
+ return nr_ice_candidate_tcp_type_names[tcp_type];
+}
+
+static int nr_ice_candidate_format_stun_label(char *label, size_t size, nr_ice_candidate *cand)
+ {
+ int _status;
+
+ *label = 0;
+ switch(cand->stun_server->type) {
+ case NR_ICE_STUN_SERVER_TYPE_ADDR:
+ snprintf(label, size, "%s(%s|%s)", nr_ctype_name(cand->type), cand->base.as_string,
+ cand->stun_server->u.addr.as_string);
+ break;
+ case NR_ICE_STUN_SERVER_TYPE_DNSNAME:
+ snprintf(label, size, "%s(%s|%s:%u)", nr_ctype_name(cand->type), cand->base.as_string,
+ cand->stun_server->u.dnsname.host, cand->stun_server->u.dnsname.port);
+ break;
+ default:
+ assert(0);
+ ABORT(R_BAD_ARGS);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_candidate_create(nr_ice_ctx *ctx,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp)
+ {
+ assert(!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) || ctype == RELAYED);
+ nr_ice_candidate *cand=0;
+ nr_ice_candidate *tmp=0;
+ int r,_status;
+ char label[512];
+
+ if(!(cand=RCALLOC(sizeof(nr_ice_candidate))))
+ ABORT(R_NO_MEMORY);
+ cand->state=NR_ICE_CAND_STATE_CREATED;
+ cand->ctx=ctx;
+ cand->isock=isock;
+ cand->osock=osock;
+ cand->type=ctype;
+ cand->tcp_type=tcp_type;
+ cand->stun_server=stun_server;
+ cand->component_id=component_id;
+ cand->component=comp;
+ cand->stream=comp->stream;
+
+ /* Extract the addr as the base */
+ if(r=nr_socket_getaddr(cand->isock->sock,&cand->base))
+ ABORT(r);
+
+ switch(ctype) {
+ case HOST:
+ snprintf(label, sizeof(label), "host(%s)", cand->base.as_string);
+ break;
+
+ case SERVER_REFLEXIVE:
+ if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand))
+ ABORT(r);
+ break;
+
+ case RELAYED:
+ if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand))
+ ABORT(r);
+ break;
+
+ case PEER_REFLEXIVE:
+ snprintf(label, sizeof(label), "prflx");
+ break;
+
+ default:
+ assert(0); /* Can't happen */
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (tcp_type) {
+ const char* ttype = nr_tcp_type_name(tcp_type);
+ const int tlen = strlen(ttype)+1; /* plus space */
+ const size_t llen=strlen(label);
+ if (snprintf(label+llen, sizeof(label)-llen, " %s", ttype) != tlen) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): truncated tcp type added to buffer",
+ ctx->label);
+ }
+ }
+
+ if(!(cand->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+
+ if(r=nr_ice_get_foundation(ctx,cand))
+ ABORT(r);
+ if(r=nr_ice_candidate_compute_priority(cand))
+ ABORT(r);
+
+ TAILQ_FOREACH(tmp,&isock->candidates,entry_sock){
+ if(cand->priority==tmp->priority){
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): duplicate priority %u candidate %s and candidate %s",
+ ctx->label,cand->priority,cand->label,tmp->label);
+ }
+ }
+
+ if(ctype==RELAYED)
+ cand->u.relayed.turn_sock=osock;
+
+
+ /* Add the candidate to the isock list*/
+ TAILQ_INSERT_TAIL(&isock->candidates,cand,entry_sock);
+
+ nr_ice_candidate_compute_codeword(cand);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): created candidate %s with type %s",
+ ctx->label,cand->codeword,cand->label,nr_ctype_name(ctype));
+
+ *candp=cand;
+
+ _status=0;
+ abort:
+ if (_status){
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to create candidate of type %s", ctx->label,nr_ctype_name(ctype));
+ nr_ice_candidate_destroy(&cand);
+ }
+ return(_status);
+ }
+
+
+/* Create a peer reflexive candidate */
+int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp)
+ {
+ nr_ice_candidate *cand=0;
+ nr_ice_candidate_type ctype=PEER_REFLEXIVE;
+ int r,_status;
+
+ if(!(cand=RCALLOC(sizeof(nr_ice_candidate))))
+ ABORT(R_NO_MEMORY);
+ if(!(cand->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+
+ cand->state=NR_ICE_CAND_STATE_INITIALIZED;
+ cand->ctx=ctx;
+ cand->type=ctype;
+ cand->component_id=comp->component_id;
+ cand->component=comp;
+ cand->stream=comp->stream;
+
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): creating candidate with type %s",
+ ctx->label,label,nr_ctype_name(ctype));
+
+ if(r=nr_transport_addr_copy(&cand->base,addr))
+ ABORT(r);
+ if(r=nr_transport_addr_copy(&cand->addr,addr))
+ ABORT(r);
+ /* Bogus foundation */
+ if(!(cand->foundation=r_strdup(cand->addr.as_string)))
+ ABORT(R_NO_MEMORY);
+
+ nr_ice_candidate_compute_codeword(cand);
+
+ *candp=cand;
+
+ _status=0;
+ abort:
+ if (_status){
+ nr_ice_candidate_destroy(&cand);
+ }
+ return(_status);
+ }
+
+static void nr_ice_candidate_mark_done(nr_ice_candidate *cand, int state)
+ {
+ if (!cand || !cand->done_cb) {
+ assert(0);
+ return;
+ }
+
+ /* If this is a relay candidate, there's likely to be a srflx that is
+ * piggybacking on it. Make sure it is marked done too. */
+ if ((cand->type == RELAYED) && cand->u.relayed.srvflx_candidate) {
+ nr_ice_candidate *srflx=cand->u.relayed.srvflx_candidate;
+ if (state == NR_ICE_CAND_STATE_INITIALIZED &&
+ nr_turn_client_get_mapped_address(cand->u.relayed.turn,
+ &srflx->addr)) {
+ r_log(LOG_ICE, LOG_WARNING, "ICE(%s)/CAND(%s): Failed to get mapped address from TURN allocate response, srflx failed.", cand->ctx->label, cand->label);
+ nr_ice_candidate_mark_done(srflx, NR_ICE_CAND_STATE_FAILED);
+ } else {
+ nr_ice_candidate_mark_done(srflx, state);
+ }
+ }
+
+ NR_async_cb done_cb=cand->done_cb;
+ cand->done_cb=0;
+ cand->state=state;
+ /* This might destroy cand! */
+ done_cb(0,0,cand->cb_arg);
+ }
+
+int nr_ice_candidate_destroy(nr_ice_candidate **candp)
+ {
+ nr_ice_candidate *cand=0;
+
+ if(!candp || !*candp)
+ return(0);
+
+ cand=*candp;
+
+ if (cand->state == NR_ICE_CAND_STATE_INITIALIZING) {
+ /* Make sure the ICE ctx isn't still waiting around for this candidate
+ * to init. */
+ nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED);
+ }
+
+ switch(cand->type){
+ case HOST:
+ break;
+#ifdef USE_TURN
+ case RELAYED:
+ if (cand->u.relayed.turn_handle)
+ nr_ice_socket_deregister(cand->isock, cand->u.relayed.turn_handle);
+ if (cand->u.relayed.srvflx_candidate)
+ cand->u.relayed.srvflx_candidate->u.srvrflx.relay_candidate=0;
+ nr_turn_client_ctx_destroy(&cand->u.relayed.turn);
+ nr_socket_destroy(&cand->u.relayed.turn_sock);
+ break;
+#endif /* USE_TURN */
+ case SERVER_REFLEXIVE:
+ if (cand->u.srvrflx.stun_handle)
+ nr_ice_socket_deregister(cand->isock, cand->u.srvrflx.stun_handle);
+ if (cand->u.srvrflx.relay_candidate)
+ cand->u.srvrflx.relay_candidate->u.relayed.srvflx_candidate=0;
+ nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun);
+ break;
+ default:
+ break;
+ }
+
+ NR_async_timer_cancel(cand->delay_timer);
+ NR_async_timer_cancel(cand->ready_cb_timer);
+ if(cand->resolver_handle){
+ nr_resolver_cancel(cand->ctx->resolver,cand->resolver_handle);
+ }
+
+ RFREE(cand->foundation);
+ RFREE(cand->label);
+ RFREE(cand);
+
+ return(0);
+ }
+
+/* This algorithm is not super-fast, but I don't think we need a hash
+ table just yet and it produces a small foundation string */
+static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand)
+ {
+ nr_ice_foundation *foundation;
+ int i=0;
+ char fnd[20];
+ int _status;
+
+ foundation=STAILQ_FIRST(&ctx->foundations);
+ while(foundation){
+ if(nr_transport_addr_cmp(&cand->base,&foundation->addr,NR_TRANSPORT_ADDR_CMP_MODE_ADDR))
+ goto next;
+ if(cand->type != foundation->type)
+ goto next;
+ if(cand->stun_server != foundation->stun_server)
+ goto next;
+
+ snprintf(fnd,sizeof(fnd),"%d",i);
+ if(!(cand->foundation=r_strdup(fnd)))
+ ABORT(R_NO_MEMORY);
+ return(0);
+
+ next:
+ foundation=STAILQ_NEXT(foundation,entry);
+ i++;
+ }
+
+ if(!(foundation=RCALLOC(sizeof(nr_ice_foundation))))
+ ABORT(R_NO_MEMORY);
+ nr_transport_addr_copy(&foundation->addr,&cand->base);
+ foundation->type=cand->type;
+ foundation->stun_server=cand->stun_server;
+ STAILQ_INSERT_TAIL(&ctx->foundations,foundation,entry);
+
+ snprintf(fnd,sizeof(fnd),"%d",i);
+ if(!(cand->foundation=r_strdup(fnd)))
+ ABORT(R_NO_MEMORY);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_candidate_compute_priority(nr_ice_candidate *cand)
+ {
+ UCHAR type_preference;
+ UCHAR interface_preference;
+ UCHAR stun_priority;
+ UCHAR direction_priority=0;
+ int r,_status;
+
+ if (cand->base.protocol != IPPROTO_UDP && cand->base.protocol != IPPROTO_TCP){
+ r_log(LOG_ICE,LOG_ERR,"Unknown protocol type %u",
+ (unsigned int)cand->base.protocol);
+ ABORT(R_INTERNAL);
+ }
+
+ switch(cand->type){
+ case HOST:
+ if(cand->base.protocol == IPPROTO_UDP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST,&type_preference))
+ ABORT(r);
+ } else if(cand->base.protocol == IPPROTO_TCP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST_TCP,&type_preference))
+ ABORT(r);
+ }
+ stun_priority=0;
+ break;
+ case RELAYED:
+ if(cand->base.protocol == IPPROTO_UDP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED,&type_preference))
+ ABORT(r);
+ } else if(cand->base.protocol == IPPROTO_TCP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED_TCP,&type_preference))
+ ABORT(r);
+ }
+ stun_priority=31-cand->stun_server->id;
+ break;
+ case SERVER_REFLEXIVE:
+ if(cand->base.protocol == IPPROTO_UDP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX,&type_preference))
+ ABORT(r);
+ } else if(cand->base.protocol == IPPROTO_TCP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP,&type_preference))
+ ABORT(r);
+ }
+ stun_priority=31-cand->stun_server->id;
+ break;
+ case PEER_REFLEXIVE:
+ if(cand->base.protocol == IPPROTO_UDP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX,&type_preference))
+ ABORT(r);
+ } else if(cand->base.protocol == IPPROTO_TCP) {
+ if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP,&type_preference))
+ ABORT(r);
+ }
+ stun_priority=0;
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ if(cand->base.protocol == IPPROTO_TCP){
+ switch (cand->tcp_type) {
+ case TCP_TYPE_ACTIVE:
+ if (cand->type == HOST)
+ direction_priority=6;
+ else
+ direction_priority=4;
+ break;
+ case TCP_TYPE_PASSIVE:
+ if (cand->type == HOST)
+ direction_priority=4;
+ else
+ direction_priority=2;
+ break;
+ case TCP_TYPE_SO:
+ if (cand->type == HOST)
+ direction_priority=2;
+ else
+ direction_priority=6;
+ break;
+ case TCP_TYPE_NONE:
+ break;
+ case TCP_TYPE_MAX:
+ default:
+ assert(0);
+ ABORT(R_INTERNAL);
+ }
+ }
+
+ if(type_preference > 126)
+ r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference);
+
+ if(!cand->ctx->interface_prioritizer) {
+ /* Prioritizer is not set, read from registry */
+ if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,
+ &interface_preference)) {
+ if (r==R_NOT_FOUND) {
+ if (next_automatic_preference == 1) {
+ r_log(LOG_ICE,LOG_ERR,"Out of preference values. Can't assign one for interface %s",cand->base.ifname);
+ ABORT(R_NOT_FOUND);
+ }
+ r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname,
+ next_automatic_preference);
+ if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){
+ ABORT(r);
+ }
+ interface_preference=next_automatic_preference << 1;
+ next_automatic_preference--;
+ if (cand->base.ip_version == NR_IPV6) {
+ /* Prefer IPV6 over IPV4 on the same interface. */
+ interface_preference += 1;
+ }
+ }
+ else {
+ ABORT(r);
+ }
+ }
+ }
+ else {
+ char key_of_interface[MAXIFNAME + 41];
+ nr_transport_addr addr;
+
+ if(r=nr_socket_getaddr(cand->isock->sock, &addr))
+ ABORT(r);
+
+ if(r=nr_transport_addr_fmt_ifname_addr_string(&addr,key_of_interface,
+ sizeof(key_of_interface))) {
+ ABORT(r);
+ }
+ if(r=nr_interface_prioritizer_get_priority(cand->ctx->interface_prioritizer,
+ key_of_interface,&interface_preference)) {
+ ABORT(r);
+ }
+ }
+
+ assert(stun_priority < 32);
+ assert(direction_priority < 8);
+
+ cand->priority=
+ (type_preference << 24) |
+ (interface_preference << 16) |
+ (direction_priority << 13) |
+ (stun_priority << 8) |
+ (256 - cand->component_id);
+
+ /* S 4.1.2 */
+ assert(cand->priority>=1&&cand->priority<=2147483647);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static void nr_ice_candidate_fire_ready_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_candidate *cand = cb_arg;
+
+ cand->ready_cb_timer = 0;
+ cand->ready_cb(0, 0, cand->ready_cb_arg);
+ }
+
+int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg)
+ {
+ int r,_status;
+ int protocol=NR_RESOLVE_PROTOCOL_STUN;
+ cand->done_cb=ready_cb;
+ cand->cb_arg=cb_arg;
+ cand->state=NR_ICE_CAND_STATE_INITIALIZING;
+
+ switch(cand->type){
+ case HOST:
+ if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr))
+ ABORT(r);
+ cand->osock=cand->isock->sock;
+ // Post this so that it doesn't happen in-line
+ cand->ready_cb = ready_cb;
+ cand->ready_cb_arg = cb_arg;
+ NR_ASYNC_TIMER_SET(0, nr_ice_candidate_fire_ready_cb, (void *)cand, &cand->ready_cb_timer);
+ break;
+#ifdef USE_TURN
+ case RELAYED:
+ protocol=NR_RESOLVE_PROTOCOL_TURN;
+ /* Fall through */
+#endif
+ case SERVER_REFLEXIVE:
+ if(cand->stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
+ if(nr_transport_addr_cmp(&cand->base,&cand->stun_server->u.addr,NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL)) {
+ r_log(LOG_ICE, LOG_INFO, "ICE-CANDIDATE(%s): Skipping srflx/relayed candidate because of IP version/transport mis-match with STUN/TURN server (%u/%s - %u/%s).", cand->label,cand->base.ip_version,cand->base.protocol==IPPROTO_UDP?"UDP":"TCP",cand->stun_server->u.addr.ip_version,cand->stun_server->u.addr.protocol==IPPROTO_UDP?"UDP":"TCP");
+ ABORT(R_NOT_FOUND); /* Same error code when DNS lookup fails */
+ }
+
+ /* Just copy the address */
+ if (r=nr_transport_addr_copy(&cand->stun_server_addr,
+ &cand->stun_server->u.addr)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not copy STUN server addr", cand->label);
+ ABORT(r);
+ }
+
+ if(r=nr_ice_candidate_initialize2(cand))
+ ABORT(r);
+ }
+ else {
+ nr_resolver_resource resource;
+ resource.domain_name=cand->stun_server->u.dnsname.host;
+ resource.port=cand->stun_server->u.dnsname.port;
+ resource.stun_turn=protocol;
+ resource.transport_protocol=cand->stun_server->transport;
+
+ switch (cand->base.ip_version) {
+ case NR_IPV4:
+ resource.address_family=AF_INET;
+ break;
+ case NR_IPV6:
+ resource.address_family=AF_INET6;
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ /* Try to resolve */
+ if(!cand->ctx->resolver) {
+ r_log(LOG_ICE, LOG_ERR, "ICE-CANDIDATE(%s): Can't use DNS names without a resolver", cand->label);
+ ABORT(R_BAD_ARGS);
+ }
+
+ if(r=nr_resolver_resolve(cand->ctx->resolver,
+ &resource,
+ nr_ice_candidate_resolved_cb,
+ (void *)cand,
+ &cand->resolver_handle)){
+ r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not invoke DNS resolver",cand->label);
+ ABORT(r);
+ }
+ }
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ nr_ice_candidate_compute_codeword(cand);
+
+ _status=0;
+ abort:
+ if(_status && _status!=R_WOULDBLOCK)
+ nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED);
+ return(_status);
+ }
+
+
+static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr)
+ {
+ nr_ice_candidate *cand=cb_arg;
+ int r,_status;
+
+ cand->resolver_handle=0;
+
+ if(addr){
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): resolved candidate %s. addr=%s",
+ cand->ctx->label,cand->label,addr->as_string);
+ }
+ else {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to resolve candidate %s.",
+ cand->ctx->label,cand->label);
+ ABORT(R_NOT_FOUND);
+ }
+
+ /* Copy the address */
+ if(r=nr_transport_addr_copy(&cand->stun_server_addr,addr))
+ ABORT(r);
+
+ if (cand->tcp_type == TCP_TYPE_PASSIVE || cand->tcp_type == TCP_TYPE_SO){
+ if (r=nr_socket_multi_tcp_stun_server_connect(cand->osock, addr))
+ ABORT(r);
+ }
+
+ /* Now start initializing */
+ if(r=nr_ice_candidate_initialize2(cand))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if(_status && _status!=R_WOULDBLOCK) {
+ nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED);
+ }
+ return(_status);
+ }
+
+static int nr_ice_candidate_initialize2(nr_ice_candidate *cand)
+ {
+ int r,_status;
+
+ switch(cand->type){
+ case HOST:
+ assert(0); /* Can't happen */
+ ABORT(R_INTERNAL);
+ break;
+#ifdef USE_TURN
+ case RELAYED:
+ if(r=nr_ice_start_relay_turn(cand))
+ ABORT(r);
+ ABORT(R_WOULDBLOCK);
+ break;
+#endif /* USE_TURN */
+ case SERVER_REFLEXIVE:
+ /* Need to start stun */
+ if(r=nr_ice_srvrflx_start_stun(cand))
+ ABORT(r);
+ cand->osock=cand->isock->sock;
+ ABORT(R_WOULDBLOCK);
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_candidate *cand=cb_arg;
+ int r,_status;
+
+ cand->delay_timer=0;
+
+/* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart
+ * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the
+ * TODO: server may not have understood the 0.96-style request */
+ if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH, nr_ice_srvrflx_stun_finished_cb, cand))
+ ABORT(r);
+
+ if(r=nr_ice_ctx_remember_id(cand->ctx, cand->u.srvrflx.stun->request))
+ ABORT(r);
+
+ if(r=nr_ice_socket_register_stun_client(cand->isock,cand->u.srvrflx.stun,&cand->u.srvrflx.stun_handle))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if (_status && (cand->u.srvrflx.stun->state==NR_STUN_CLIENT_STATE_RUNNING)) {
+ nr_stun_client_failed(cand->u.srvrflx.stun);
+ }
+ return;
+ }
+
+static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand)
+ {
+ int r,_status;
+
+ assert(!cand->delay_timer);
+ if(r=nr_stun_client_ctx_create(cand->label, cand->isock->sock,
+ &cand->stun_server_addr, cand->stream->ctx->gather_rto,
+ &cand->u.srvrflx.stun))
+ ABORT(r);
+
+ NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_srvrflx_start_stun_timer_cb,cand,&cand->delay_timer);
+ cand->stream->ctx->stun_delay += cand->stream->ctx->Ta;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+#ifdef USE_TURN
+static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_candidate *cand=cb_arg;
+ int r,_status;
+
+ cand->delay_timer=0;
+
+ if(r=nr_turn_client_allocate(cand->u.relayed.turn, nr_ice_turn_allocated_cb, cb_arg))
+ ABORT(r);
+
+ if(r=nr_ice_socket_register_turn_client(cand->isock, cand->u.relayed.turn,
+ cand->osock, &cand->u.relayed.turn_handle))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if(_status && (cand->u.relayed.turn->state==NR_TURN_CLIENT_STATE_ALLOCATING)){
+ nr_turn_client_failed(cand->u.relayed.turn);
+ }
+ return;
+ }
+
+static int nr_ice_start_relay_turn(nr_ice_candidate *cand)
+ {
+ int r,_status;
+ assert(!cand->delay_timer);
+ if(r=nr_turn_client_ctx_create(cand->label, cand->isock->sock,
+ cand->u.relayed.server->username,
+ cand->u.relayed.server->password,
+ &cand->stun_server_addr,
+ &cand->u.relayed.turn))
+ ABORT(r);
+
+ if(r=nr_socket_turn_set_ctx(cand->osock, cand->u.relayed.turn))
+ ABORT(r);
+
+ NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_start_relay_turn_timer_cb,cand,&cand->delay_timer);
+ cand->stream->ctx->stun_delay += cand->stream->ctx->Ta;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+#endif /* USE_TURN */
+
+static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg)
+ {
+ int _status;
+ nr_ice_candidate *cand=cb_arg;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): %s",cand->ctx->label,cand->label,__FUNCTION__);
+
+ /* Deregister to suppress duplicates */
+ if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */
+ nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle);
+ cand->u.srvrflx.stun_handle=0;
+ }
+
+ switch(cand->u.srvrflx.stun->state){
+ /* OK, we should have a mapped address */
+ case NR_STUN_CLIENT_STATE_DONE:
+ /* Copy the address */
+ nr_transport_addr_copy(&cand->addr, &cand->u.srvrflx.stun->results.stun_binding_response.mapped_addr);
+ cand->addr.protocol=cand->base.protocol;
+ nr_transport_addr_fmt_addr_string(&cand->addr);
+ nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun);
+ nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED);
+ cand=0;
+ break;
+
+ /* This failed, so go to the next STUN server if there is one */
+ case NR_STUN_CLIENT_STATE_FAILED:
+ ABORT(R_NOT_FOUND);
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+ _status = 0;
+ abort:
+ if(_status){
+ nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED);
+ }
+ }
+
+#ifdef USE_TURN
+static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ int r,_status;
+ nr_ice_candidate *cand=cb_arg;
+ nr_turn_client_ctx *turn=cand->u.relayed.turn;
+ char *label;
+ nr_transport_addr relay_addr;
+
+ switch(turn->state){
+ /* OK, we should have a mapped address */
+ case NR_TURN_CLIENT_STATE_ALLOCATED:
+ if (r=nr_turn_client_get_relayed_address(turn, &relay_addr))
+ ABORT(r);
+
+ if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",
+ relay_addr.as_string,")",NULL))
+ ABORT(r);
+
+ r_log(LOG_ICE,LOG_DEBUG,"TURN-CLIENT(%s)/CAND(%s): Switching from TURN to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label);
+
+ /* Copy the relayed address into the candidate addr and
+ into the candidate base. Note that we need to keep the
+ ifname in the base. */
+ if (r=nr_transport_addr_copy(&cand->addr, &relay_addr))
+ ABORT(r);
+ if (r=nr_transport_addr_copy_keep_ifname(&cand->base, &relay_addr)) /* Need to keep interface for priority calculation */
+ ABORT(r);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): new relay base=%s addr=%s", cand->ctx->label, cand->label, cand->base.as_string, cand->addr.as_string);
+
+ RFREE(cand->label);
+ cand->label=label;
+ nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED);
+ cand = 0;
+
+ break;
+
+ case NR_TURN_CLIENT_STATE_FAILED:
+ case NR_TURN_CLIENT_STATE_CANCELLED:
+ r_log(NR_LOG_TURN, LOG_WARNING,
+ "ICE-CANDIDATE(%s): nr_turn_allocated_cb called with state %d",
+ cand->label, turn->state);
+ /* This failed, so go to the next TURN server if there is one */
+ ABORT(R_NOT_FOUND);
+ break;
+ default:
+ assert(0); /* should never happen */
+ ABORT(R_INTERNAL);
+ }
+
+ _status=0;
+ abort:
+ if(_status){
+ if (cand) {
+ r_log(NR_LOG_TURN, LOG_WARNING,
+ "ICE-CANDIDATE(%s): nr_turn_allocated_cb failed", cand->label);
+ nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED);
+ }
+ }
+ }
+#endif /* USE_TURN */
+
+/* Format the candidate attribute as per ICE S 15.1 */
+int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen)
+ {
+ int r,_status;
+ char addr[64];
+ int port;
+ int len;
+ nr_transport_addr *raddr;
+
+ assert(!strcmp(nr_ice_candidate_type_names[HOST], "host"));
+ assert(!strcmp(nr_ice_candidate_type_names[RELAYED], "relay"));
+
+ if(r=nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr)))
+ ABORT(r);
+ if(r=nr_transport_addr_get_port(&cand->addr,&port))
+ ABORT(r);
+ /* https://tools.ietf.org/html/rfc6544#section-4.5 */
+ if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type==TCP_TYPE_ACTIVE)
+ port=9;
+ snprintf(attr,maxlen,"candidate:%s %d %s %u %s %d typ %s",
+ cand->foundation, cand->component_id, cand->addr.protocol==IPPROTO_UDP?"UDP":"TCP",cand->priority, addr, port,
+ nr_ctype_name(cand->type));
+
+ len=strlen(attr); attr+=len; maxlen-=len;
+
+ /* raddr, rport */
+ raddr = (cand->stream->ctx->flags &
+ (NR_ICE_CTX_FLAGS_RELAY_ONLY |
+ NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES)) ?
+ &cand->addr : &cand->base;
+
+ switch(cand->type){
+ case HOST:
+ break;
+ case SERVER_REFLEXIVE:
+ case PEER_REFLEXIVE:
+ if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr)))
+ ABORT(r);
+ if(r=nr_transport_addr_get_port(raddr,&port))
+ ABORT(r);
+ snprintf(attr,maxlen," raddr %s rport %d",addr,port);
+ break;
+ case RELAYED:
+ // comes from XorMappedAddress via AllocateResponse
+ if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr)))
+ ABORT(r);
+ if(r=nr_transport_addr_get_port(raddr,&port))
+ ABORT(r);
+
+ snprintf(attr,maxlen," raddr %s rport %d",addr,port);
+ break;
+ default:
+ assert(0);
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type){
+ len=strlen(attr);
+ attr+=len;
+ maxlen-=len;
+ snprintf(attr,maxlen," tcptype %s",nr_tcp_type_name(cand->tcp_type));
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h
new file mode 100644
index 000000000..16be40276
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h
@@ -0,0 +1,121 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_candidate_h
+#define _ice_candidate_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {HOST=1, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED, CTYPE_MAX} nr_ice_candidate_type;
+
+struct nr_ice_candidate_ {
+ char *label;
+ char codeword[5];
+ int state;
+#define NR_ICE_CAND_STATE_CREATED 1
+#define NR_ICE_CAND_STATE_INITIALIZING 2
+#define NR_ICE_CAND_STATE_INITIALIZED 3
+#define NR_ICE_CAND_STATE_FAILED 4
+#define NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED 9
+#define NR_ICE_CAND_PEER_CANDIDATE_PAIRED 10
+ struct nr_ice_ctx_ *ctx;
+ nr_ice_socket *isock; /* The socket to read from
+ (it contains all other candidates
+ on this socket) */
+ nr_socket *osock; /* The socket to write to */
+ nr_ice_media_stream *stream; /* The media stream this is associated with */
+ nr_ice_component *component; /* The component this is associated with */
+ nr_ice_candidate_type type; /* The type of the candidate (S 4.1.1) */
+ nr_socket_tcp_type tcp_type;
+ UCHAR component_id; /* The component id (S 4.1.2.1) */
+ nr_transport_addr addr; /* The advertised address;
+ JDR calls this the candidate */
+ nr_transport_addr base; /* The base address (S 2.1)*/
+ char *foundation; /* Foundation for the candidate (S 4) */
+ UINT4 priority; /* The priority value (S 5.4 */
+ nr_ice_stun_server *stun_server;
+ nr_transport_addr stun_server_addr; /* Resolved STUN server address */
+ void *delay_timer;
+ void *resolver_handle;
+
+ /* Holding data for STUN and TURN */
+ union {
+ struct {
+ nr_stun_client_ctx *stun;
+ void *stun_handle;
+ /* If this is a srflx that is piggybacking on a relay candidate, this is
+ * a back pointer to that relay candidate. */
+ nr_ice_candidate *relay_candidate;
+ } srvrflx;
+ struct {
+ nr_turn_client_ctx *turn;
+ nr_ice_turn_server *server;
+ nr_ice_candidate *srvflx_candidate;
+ nr_socket *turn_sock;
+ void *turn_handle;
+ } relayed;
+ } u;
+
+ NR_async_cb done_cb;
+ void *cb_arg;
+
+ NR_async_cb ready_cb;
+ void *ready_cb_arg;
+ void *ready_cb_timer;
+
+ TAILQ_ENTRY(nr_ice_candidate_) entry_sock;
+ TAILQ_ENTRY(nr_ice_candidate_) entry_comp;
+};
+
+extern char *nr_ice_candidate_type_names[];
+extern char *nr_ice_candidate_tcp_type_names[];
+
+
+int nr_ice_candidate_create(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp);
+int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg);
+void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand);
+int nr_ice_candidate_process_stun(nr_ice_candidate *cand, UCHAR *msg, int len, nr_transport_addr *faddr);
+int nr_ice_candidate_destroy(nr_ice_candidate **candp);
+int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen);
+int nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *attr,nr_ice_media_stream *stream,nr_ice_candidate **candp);
+int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp);
+int nr_ice_candidate_compute_priority(nr_ice_candidate *cand);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
new file mode 100644
index 000000000..6c07ae2a6
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c
@@ -0,0 +1,684 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_candidate_pair.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <assert.h>
+#include <string.h>
+#include <nr_api.h>
+#include "async_timer.h"
+#include "ice_ctx.h"
+#include "ice_util.h"
+#include "ice_codeword.h"
+#include "stun.h"
+
+static char *nr_ice_cand_pair_states[]={"UNKNOWN","FROZEN","WAITING","IN_PROGRESS","FAILED","SUCCEEDED","CANCELLED"};
+
+static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg);
+static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair,
+ nr_ice_candidate *lcand, nr_ice_candidate *rcand);
+
+static void nr_ice_candidate_pair_set_priority(nr_ice_cand_pair *pair)
+ {
+ /* Priority computation S 5.7.2 */
+ UINT8 controlling_priority, controlled_priority;
+ if(pair->pctx->controlling)
+ {
+ controlling_priority=pair->local->priority;
+ controlled_priority=pair->remote->priority;
+ }
+ else{
+ controlling_priority=pair->remote->priority;
+ controlled_priority=pair->local->priority;
+ }
+ pair->priority=(MIN(controlling_priority, controlled_priority))<<32 |
+ (MAX(controlling_priority, controlled_priority))<<1 |
+ (controlled_priority > controlling_priority?0:1);
+ }
+
+int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp)
+ {
+ nr_ice_cand_pair *pair=0;
+ int r,_status;
+ UINT4 RTO;
+ nr_ice_candidate tmpcand;
+ UINT8 t_priority;
+
+ if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair))))
+ ABORT(R_NO_MEMORY);
+
+ pair->pctx=pctx;
+
+ nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand);
+
+ if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|",
+ rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL))
+ ABORT(r);
+
+ nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
+ pair->local=lcand;
+ pair->remote=rcand;
+
+ nr_ice_candidate_pair_set_priority(pair);
+
+ /*
+ TODO(bcampen@mozilla.com): Would be nice to log why this candidate was
+ created (initial pair generation, triggered check, and new trickle
+ candidate seem to be the possibilities here).
+ */
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s)/CAND-PAIR(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx)",pctx->ctx->label,pair->codeword,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority);
+
+ /* Foundation */
+ if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|",
+ rcand->foundation,NULL))
+ ABORT(r);
+
+ /* Compute the RTO per S 16 */
+ RTO = MAX(100, (pctx->ctx->Ta * pctx->waiting_pairs));
+
+ /* Make a bogus candidate to compute a theoretical peer reflexive
+ * priority per S 7.1.1.1 */
+ memcpy(&tmpcand, lcand, sizeof(tmpcand));
+ tmpcand.type = PEER_REFLEXIVE;
+ if (r=nr_ice_candidate_compute_priority(&tmpcand))
+ ABORT(r);
+ t_priority = tmpcand.priority;
+
+ /* Our sending context */
+ if(r=nr_stun_client_ctx_create(pair->as_string,
+ lcand->osock,
+ &rcand->addr,RTO,&pair->stun_client))
+ ABORT(r);
+ if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(rcand->stream->l2r_user)))
+ ABORT(R_NO_MEMORY);
+ if(r=r_data_copy(&pair->stun_client->params.ice_binding_request.password,
+ &rcand->stream->l2r_pass))
+ ABORT(r);
+ /* TODO(ekr@rtfm.com): Do we need to frob this when we change role. Bug 890667 */
+ pair->stun_client->params.ice_binding_request.control = pctx->controlling?
+ NR_ICE_CONTROLLING:NR_ICE_CONTROLLED;
+ pair->stun_client->params.ice_binding_request.priority=t_priority;
+
+ pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker;
+
+ *pairp=pair;
+
+ _status=0;
+ abort:
+ if(_status){
+ nr_ice_candidate_pair_destroy(&pair);
+ }
+ return(_status);
+ }
+
+int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp)
+ {
+ nr_ice_cand_pair *pair;
+
+ if(!pairp || !*pairp)
+ return(0);
+
+ pair=*pairp;
+ *pairp=0;
+
+ RFREE(pair->as_string);
+ RFREE(pair->foundation);
+ nr_ice_socket_deregister(pair->local->isock,pair->stun_client_handle);
+ if (pair->stun_client) {
+ RFREE(pair->stun_client->params.ice_binding_request.username);
+ RFREE(pair->stun_client->params.ice_binding_request.password.data);
+ nr_stun_client_ctx_destroy(&pair->stun_client);
+ }
+
+ NR_async_timer_cancel(pair->stun_cb_timer);
+ NR_async_timer_cancel(pair->restart_role_change_cb_timer);
+ NR_async_timer_cancel(pair->restart_nominated_cb_timer);
+
+ RFREE(pair);
+ return(0);
+ }
+
+int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
+ {
+ assert(pair->state==NR_ICE_PAIR_STATE_FROZEN);
+
+ nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
+
+ return(0);
+ }
+
+static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ int r,_status;
+ nr_ice_cand_pair *pair=cb_arg;
+ nr_ice_cand_pair *actual_pair=0;
+ nr_ice_candidate *cand=0;
+ nr_stun_message *sres;
+ nr_transport_addr *request_src;
+ nr_transport_addr *request_dst;
+ nr_transport_addr *response_src;
+ nr_transport_addr response_dst;
+ nr_stun_message_attribute *attr;
+
+ pair->stun_cb_timer=0;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb on pair addr = %s",
+ pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string);
+
+ /* This ordinarily shouldn't happen, but can if we're
+ doing the second check to confirm nomination.
+ Just bail out */
+ if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED)
+ goto done;
+
+ switch(pair->stun_client->state){
+ case NR_STUN_CLIENT_STATE_FAILED:
+ sres=pair->stun_client->response;
+ if(sres && nr_stun_message_has_attribute(sres,NR_STUN_ATTR_ERROR_CODE,&attr)&&attr->u.error_code.number==487){
+
+ /*
+ * Flip the controlling bit; subsequent 487s for other pairs will be
+ * ignored, since we abandon their STUN transactions.
+ */
+ nr_ice_peer_ctx_switch_controlling_role(pair->pctx);
+
+ return;
+ }
+ /* Fall through */
+ case NR_STUN_CLIENT_STATE_TIMED_OUT:
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
+ break;
+ case NR_STUN_CLIENT_STATE_DONE:
+ /* make sure the addresses match up S 7.1.2.2 */
+ response_src=&pair->stun_client->peer_addr;
+ request_dst=&pair->remote->addr;
+ if (nr_transport_addr_cmp(response_src,request_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Peer address mismatch %s != %s",pair->pctx->label,pair->codeword,response_src->as_string,request_dst->as_string);
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
+ break;
+ }
+ request_src=&pair->stun_client->my_addr;
+ nr_socket_getaddr(pair->local->osock,&response_dst);
+ if (nr_transport_addr_cmp(request_src,&response_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Local address mismatch %s != %s",pair->pctx->label,pair->codeword,request_src->as_string,response_dst.as_string);
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
+ break;
+ }
+
+ if(strlen(pair->stun_client->results.ice_binding_response.mapped_addr.as_string)==0){
+ /* we're using the mapped_addr returned by the server to lookup our
+ * candidate, but if the server fails to do that we can't perform
+ * the lookup -- this may be a BUG because if we've gotten here
+ * then the transaction ID check succeeded, and perhaps we should
+ * just assume that it's the server we're talking to and that our
+ * peer is ok, but I'm not sure how that'll interact with the
+ * peer reflexive logic below */
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): server failed to return mapped address on pair %s", pair->pctx->label,pair->codeword,pair->as_string);
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
+ break;
+ }
+ else if(!nr_transport_addr_cmp(&pair->local->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED);
+ }
+ else if(pair->stun_client->state == NR_STUN_CLIENT_STATE_DONE) {
+ /* OK, this didn't correspond to a pair on the check list, but
+ it probably matches one of our candidates */
+
+ cand=TAILQ_FIRST(&pair->local->component->candidates);
+ while(cand){
+ if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): found pre-existing local candidate of type %d for mapped address %s", pair->pctx->label,cand->type,cand->addr.as_string);
+ assert(cand->type != HOST);
+ break;
+ }
+
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+
+ if(!cand) {
+ /* OK, nothing found, must be a new peer reflexive */
+ if (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) {
+ /* Any STUN response with a reflexive address in it is unwanted
+ when we'll send on relay only. Bail since cand is used below. */
+ goto done;
+ }
+ if(r=nr_ice_candidate_create(pair->pctx->ctx,
+ pair->local->component,pair->local->isock,pair->local->osock,
+ PEER_REFLEXIVE,pair->local->tcp_type,0,pair->local->component->component_id,&cand))
+ ABORT(r);
+ if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr))
+ ABORT(r);
+ cand->state=NR_ICE_CAND_STATE_INITIALIZED;
+ TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp);
+ } else {
+ /* Check if we have a pair for this candidate already. */
+ if(r=nr_ice_media_stream_find_pair(pair->remote->stream, cand, pair->remote, &actual_pair)) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no pair exists for %s and %s", pair->pctx->label,cand->addr.as_string, pair->remote->addr.as_string);
+ }
+ }
+
+ if(!actual_pair) {
+ if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &actual_pair))
+ ABORT(r);
+
+ if(r=nr_ice_component_insert_pair(actual_pair->remote->component,actual_pair))
+ ABORT(r);
+
+ /* If the original pair was nominated, make us nominated too. */
+ if(pair->peer_nominated)
+ actual_pair->peer_nominated=1;
+
+ /* Now mark the orig pair failed */
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED);
+ }
+
+ assert(actual_pair);
+ nr_ice_candidate_pair_set_state(actual_pair->pctx,actual_pair,NR_ICE_PAIR_STATE_SUCCEEDED);
+ pair=actual_pair;
+
+ }
+
+ /* Should we set nominated? */
+ if(pair->pctx->controlling){
+ if(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)
+ pair->nominated=1;
+ }
+ else{
+ if(pair->peer_nominated)
+ pair->nominated=1;
+ }
+
+
+ /* increment the number of valid pairs in the component */
+ /* We don't bother to maintain a separate valid list */
+ pair->remote->component->valid_pairs++;
+
+ /* S 7.1.2.2: unfreeze other pairs with the same foundation*/
+ if(r=nr_ice_media_stream_unfreeze_pairs_foundation(pair->remote->stream,pair->foundation))
+ ABORT(r);
+
+ /* Deal with this pair being nominated */
+ if(pair->nominated){
+ if(r=nr_ice_component_nominated_pair(pair->remote->component, pair))
+ ABORT(r);
+ }
+
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ /* If we're controlling but in regular mode, ask the handler
+ if he wants to nominate something and stop... */
+ if(pair->pctx->controlling && !(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)){
+
+ if(r=nr_ice_component_select_pair(pair->pctx,pair->remote->component)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+ }
+
+ done:
+ _status=0;
+ abort:
+ return;
+ }
+
+static void nr_ice_candidate_pair_restart(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+ UINT4 mode;
+
+ nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_IN_PROGRESS);
+
+ /* Start STUN */
+ if(pair->pctx->controlling && (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION))
+ mode=NR_ICE_CLIENT_MODE_USE_CANDIDATE;
+ else
+ mode=NR_ICE_CLIENT_MODE_BINDING_REQUEST;
+
+ nr_stun_client_reset(pair->stun_client);
+
+ if(r=nr_stun_client_start(pair->stun_client,mode,nr_ice_candidate_pair_stun_cb,pair))
+ ABORT(r);
+
+ if ((r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))) {
+ /* ignore if this fails (which it shouldn't) because it's only an
+ * optimization and the cleanup routines are not going to do the right
+ * thing if this fails */
+ assert(0);
+ }
+
+ _status=0;
+ abort:
+ if(_status){
+ /* Don't fire the CB, but schedule it to fire ASAP */
+ assert(!pair->stun_cb_timer);
+ NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_stun_cb,pair, &pair->stun_cb_timer);
+ _status=0;
+ }
+ }
+
+int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+
+ /* Register the stun ctx for when responses come in*/
+ if(r=nr_ice_socket_register_stun_client(pair->local->isock,pair->stun_client,&pair->stun_client_handle))
+ ABORT(r);
+
+ nr_ice_candidate_pair_restart(pctx, pair);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_candidate_copy_for_triggered_check(nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+ nr_ice_cand_pair *copy;
+
+ if(r=nr_ice_candidate_pair_create(pair->pctx, pair->local, pair->remote, &copy))
+ ABORT(r);
+
+ /* Preserve nomination status */
+ copy->peer_nominated= pair->peer_nominated;
+ copy->nominated = pair->nominated;
+
+ r_log(LOG_ICE,LOG_INFO,"CAND-PAIR(%s): Adding pair to check list and trigger check queue: %s",pair->codeword,pair->as_string);
+ if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy))
+ ABORT(r);
+ nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,copy);
+
+ copy->triggered = 1;
+ nr_ice_candidate_pair_set_state(copy->pctx,copy,NR_ICE_PAIR_STATE_WAITING);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+
+ if(pair->state==NR_ICE_PAIR_STATE_CANCELLED) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Ignoring matching but canceled pair",pctx->label,pair->codeword);
+ return(0);
+ } else if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for succeeded pair",pctx->label,pair->codeword);
+ return(0);
+ }
+
+ /* Do not run this logic more than once on a given pair */
+ if(!pair->triggered){
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): triggered check on %s",pctx->label,pair->codeword,pair->as_string);
+
+ pair->triggered=1;
+
+ switch(pair->state){
+ case NR_ICE_PAIR_STATE_FAILED:
+ /* OK, there was a pair, it's just invalid: According to Section
+ * 7.2.1.4, we need to resurrect it */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on failed pair, resurrecting: %s",pctx->label,pair->codeword,pair->as_string);
+ /* fall through */
+ case NR_ICE_PAIR_STATE_FROZEN:
+ nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
+ /* fall through even further */
+ case NR_ICE_PAIR_STATE_WAITING:
+ /* Append it additionally to the trigger check queue */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): Inserting pair to trigger check queue: %s",pctx->label,pair->codeword,pair->as_string);
+ nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,pair);
+ break;
+ case NR_ICE_PAIR_STATE_IN_PROGRESS:
+ /* Instead of trying to maintain two stun contexts on the same pair,
+ * and handling heterogenous responses and error conditions, we instead
+ * create a second pair that is identical except that it has the
+ * |triggered| bit set. We also cancel the original pair, but it can
+ * still succeed on its own in the special waiting state. */
+ if(r=nr_ice_candidate_copy_for_triggered_check(pair))
+ ABORT(r);
+ nr_ice_candidate_pair_cancel(pair->pctx,pair,1);
+ break;
+ default:
+ /* all states are handled - a new/unknown state should not
+ * automatically enter the start_checks() below */
+ assert(0);
+ break;
+ }
+
+ /* Ensure that the timers are running to start checks on the topmost entry
+ * of the triggered check queue. */
+ if(r=nr_ice_media_stream_start_checks(pair->pctx,pair->remote->stream))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state)
+ {
+ if(pair->state != NR_ICE_PAIR_STATE_FAILED){
+ /* If it's already running we need to terminate the stun */
+ if(pair->state==NR_ICE_PAIR_STATE_IN_PROGRESS){
+ if(move_to_wait_state) {
+ nr_stun_client_wait(pair->stun_client);
+ } else {
+ nr_stun_client_cancel(pair->stun_client);
+ }
+ }
+ nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_CANCELLED);
+ }
+
+ return(0);
+ }
+
+int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+
+ if(!pair){
+ r_log(LOG_ICE,LOG_ERR,"ICE-PAIR: No pair chosen");
+ ABORT(R_BAD_ARGS);
+ }
+
+ if(pair->state!=NR_ICE_PAIR_STATE_SUCCEEDED){
+ r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): tried to install non-succeeded pair, ignoring: %s",pair->pctx->label,pair->codeword,pair->as_string);
+ }
+ else{
+ /* Ok, they chose one */
+ /* 1. Send a new request with nominated. Do it as a scheduled
+ event to avoid reentrancy issues. Only do this if it hasn't
+ happened already (though this shouldn't happen.)
+ */
+ if(!pair->restart_nominated_cb_timer)
+ NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_nominated_cb,pair,&pair->restart_nominated_cb_timer);
+
+ /* 2. Tell ourselves this pair is ready */
+ if(r=nr_ice_component_nominated_pair(pair->remote->component, pair))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state)
+ {
+ int r,_status;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): setting pair to state %s: %s",
+ pctx->label,pair->codeword,nr_ice_cand_pair_states[state],pair->as_string);
+
+ /* NOTE: This function used to reference pctx->state instead of
+ pair->state and the assignment to pair->state was at the top
+ of this function. Because pctx->state was never changed, this seems to have
+ been a typo. The natural logic is "if the state changed
+ decrement the counter" so this implies we should be checking
+ the pair state rather than the pctx->state.
+
+ This didn't cause big problems because waiting_pairs was only
+ used for pacing, so the pacing just was kind of broken.
+
+ This note is here as a reminder until we do more testing
+ and make sure that in fact this was a typo.
+ */
+ if(pair->state!=NR_ICE_PAIR_STATE_WAITING){
+ if(state==NR_ICE_PAIR_STATE_WAITING)
+ pctx->waiting_pairs++;
+ }
+ else{
+ if(state!=NR_ICE_PAIR_STATE_WAITING)
+ pctx->waiting_pairs--;
+
+ assert(pctx->waiting_pairs>=0);
+ }
+ pair->state=state;
+
+
+ if(pair->state==NR_ICE_PAIR_STATE_FAILED ||
+ pair->state==NR_ICE_PAIR_STATE_CANCELLED){
+ if(r=nr_ice_component_failed_pair(pair->remote->component, pair))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out)
+ {
+ /*r_log(LOG_ICE,LOG_DEBUG,"CAND-PAIR(%s): pair %s: state=%s, priority=0x%llx\n",pair->codeword,pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority);*/
+
+ return(0);
+ }
+
+
+int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
+ {
+ if(pair->triggered_check_queue_entry.tqe_next ||
+ pair->triggered_check_queue_entry.tqe_prev)
+ return(0);
+
+ TAILQ_INSERT_TAIL(head,pair,triggered_check_queue_entry);
+
+ return(0);
+ }
+
+int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair)
+ {
+ nr_ice_cand_pair *c1;
+
+ c1=TAILQ_FIRST(head);
+ while(c1){
+ if(c1->priority < pair->priority){
+ TAILQ_INSERT_BEFORE(c1,pair,check_queue_entry);
+ break;
+ }
+
+ c1=TAILQ_NEXT(c1,check_queue_entry);
+ }
+ if(!c1) TAILQ_INSERT_TAIL(head,pair,check_queue_entry);
+
+ return(0);
+ }
+
+void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_cand_pair *pair=cb_arg;
+ int r,_status;
+
+ pair->restart_nominated_cb_timer=0;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s)/COMP(%d): Restarting pair as nominated: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string);
+
+ nr_stun_client_reset(pair->stun_client);
+
+ if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair))
+ ABORT(r);
+
+ if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return;
+ }
+
+static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_cand_pair *pair=cb_arg;
+
+ pair->restart_role_change_cb_timer=0;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s):COMP(%d): Restarting pair as %s: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->pctx->controlling ? "CONTROLLING" : "CONTROLLED",pair->as_string);
+
+ nr_ice_candidate_pair_restart(pair->pctx, pair);
+ }
+
+void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair)
+ {
+ pair->stun_client->params.ice_binding_request.control = pair->pctx->controlling ? NR_ICE_CONTROLLING : NR_ICE_CONTROLLED;
+ nr_ice_candidate_pair_set_priority(pair);
+
+ if(pair->state == NR_ICE_PAIR_STATE_IN_PROGRESS) {
+ /* We could try only restarting in-progress pairs when they receive their
+ * 487, but this ends up being simpler, because any extra 487 are dropped.
+ */
+ if(!pair->restart_role_change_cb_timer)
+ NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_role_change_cb,pair,&pair->restart_role_change_cb_timer);
+ }
+ }
+
+static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair,
+ nr_ice_candidate *lcand, nr_ice_candidate *rcand)
+ {
+ char as_string[2048];
+
+ snprintf(as_string,
+ sizeof(as_string),
+ "%s|%s(%s|%s)",
+ lcand->addr.as_string,
+ rcand->addr.as_string,
+ lcand->label,
+ rcand->label);
+
+ nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword);
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h
new file mode 100644
index 000000000..171ded4a0
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h
@@ -0,0 +1,95 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_candidate_pair_h
+#define _ice_candidate_pair_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+
+struct nr_ice_cand_pair_ {
+ nr_ice_peer_ctx *pctx;
+ char codeword[5];
+ char *as_string;
+ int state; /* The current state (S 5.7.3) */
+#define NR_ICE_PAIR_STATE_FROZEN 1
+#define NR_ICE_PAIR_STATE_WAITING 2
+#define NR_ICE_PAIR_STATE_IN_PROGRESS 3
+#define NR_ICE_PAIR_STATE_FAILED 4
+#define NR_ICE_PAIR_STATE_SUCCEEDED 5
+#define NR_ICE_PAIR_STATE_CANCELLED 6
+
+ UCHAR peer_nominated; /* The peer sent USE-CANDIDATE
+ on this check */
+ UCHAR nominated; /* Is this nominated or not */
+
+ UCHAR triggered; /* Ignore further trigger check requests */
+
+ UINT8 priority; /* The priority for this pair */
+ nr_ice_candidate *local; /* The local candidate */
+ nr_ice_candidate *remote; /* The remote candidate */
+ char *foundation; /* The combined foundations */
+
+ nr_stun_client_ctx *stun_client; /* STUN context when acting as a client */
+ void *stun_client_handle;
+
+ void *stun_cb_timer;
+ void *restart_role_change_cb_timer;
+ void *restart_nominated_cb_timer;
+
+ TAILQ_ENTRY(nr_ice_cand_pair_) check_queue_entry; /* the check list */
+ TAILQ_ENTRY(nr_ice_cand_pair_) triggered_check_queue_entry; /* the trigger check queue */
+};
+
+int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp);
+int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
+int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
+int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state);
+int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out);
+int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state);
+int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair);
+int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
+int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair);
+int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair);
+void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg);
+int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp);
+void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_codeword.h b/media/mtransport/third_party/nICEr/src/ice/ice_codeword.h
new file mode 100644
index 000000000..76fce0b69
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_codeword.h
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_codeword_h
+#define _ice_codeword_h
+
+void nr_ice_compute_codeword(char *buf, int len,char *codeword);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_component.c b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
new file mode 100644
index 000000000..2be25efca
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -0,0 +1,1688 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_component.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <string.h>
+#include <assert.h>
+#include <nr_api.h>
+#include <registry.h>
+#include <async_timer.h>
+#include "ice_ctx.h"
+#include "ice_codeword.h"
+#include "stun.h"
+#include "nr_socket_local.h"
+#include "nr_socket_turn.h"
+#include "nr_socket_wrapper.h"
+#include "nr_socket_buffered_stun.h"
+#include "nr_socket_multi_tcp.h"
+#include "ice_reg.h"
+#include "nr_crypto.h"
+#include "r_time.h"
+
+static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error);
+static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp);
+void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp);
+void nr_ice_component_consent_destroy(nr_ice_component *comp);
+
+/* This function takes ownership of the contents of req (but not req itself) */
+static int nr_ice_pre_answer_request_create(nr_transport_addr *dst, nr_stun_server_request *req, nr_ice_pre_answer_request **parp)
+ {
+ int r, _status;
+ nr_ice_pre_answer_request *par = 0;
+ nr_stun_message_attribute *attr;
+
+ if (!(par = RCALLOC(sizeof(nr_ice_pre_answer_request))))
+ ABORT(R_NO_MEMORY);
+
+ par->req = *req; /* Struct assignment */
+ memset(req, 0, sizeof(*req)); /* Zero contents to avoid confusion */
+
+ if (r=nr_transport_addr_copy(&par->local_addr, dst))
+ ABORT(r);
+ if (!nr_stun_message_has_attribute(par->req.request, NR_STUN_ATTR_USERNAME, &attr))
+ ABORT(R_INTERNAL);
+ if (!(par->username = r_strdup(attr->u.username)))
+ ABORT(R_NO_MEMORY);
+
+ *parp=par;
+ _status=0;
+ abort:
+ if (_status) {
+ /* Erase the request so we don't free it */
+ memset(&par->req, 0, sizeof(nr_stun_server_request));
+ nr_ice_pre_answer_request_destroy(&par);
+ }
+
+ return(_status);
+ }
+
+static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp)
+ {
+ nr_ice_pre_answer_request *par;
+
+ if (!parp || !*parp)
+ return(0);
+
+ par = *parp;
+ *parp = 0;
+
+ nr_stun_message_destroy(&par->req.request);
+ nr_stun_message_destroy(&par->req.response);
+
+ RFREE(par->username);
+ RFREE(par);
+
+ return(0);
+ }
+
+int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ice_component **componentp)
+ {
+ int _status;
+ nr_ice_component *comp=0;
+
+ if(!(comp=RCALLOC(sizeof(nr_ice_component))))
+ ABORT(R_NO_MEMORY);
+
+ comp->state=NR_ICE_COMPONENT_UNPAIRED;
+ comp->component_id=component_id;
+ comp->stream=stream;
+ comp->ctx=stream->ctx;
+
+ STAILQ_INIT(&comp->sockets);
+ TAILQ_INIT(&comp->candidates);
+ STAILQ_INIT(&comp->pre_answer_reqs);
+
+ STAILQ_INSERT_TAIL(&stream->components,comp,entry);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_component_destroy(nr_ice_component **componentp)
+ {
+ nr_ice_component *component;
+ nr_ice_socket *s1,*s2;
+ nr_ice_candidate *c1,*c2;
+ nr_ice_pre_answer_request *r1,*r2;
+
+ if(!componentp || !*componentp)
+ return(0);
+
+ component=*componentp;
+ *componentp=0;
+
+ nr_ice_component_consent_destroy(component);
+
+ /* Detach ourselves from the sockets */
+ if (component->local_component){
+ nr_ice_socket *isock=STAILQ_FIRST(&component->local_component->sockets);
+ while(isock){
+ nr_stun_server_remove_client(isock->stun_server, component);
+ isock=STAILQ_NEXT(isock, entry);
+ }
+ }
+
+ /* candidates MUST be destroyed before the sockets so that
+ they can deregister */
+ TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){
+ TAILQ_REMOVE(&component->candidates,c1,entry_comp);
+ nr_ice_candidate_destroy(&c1);
+ }
+
+ STAILQ_FOREACH_SAFE(s1, &component->sockets, entry, s2){
+ STAILQ_REMOVE(&component->sockets,s1,nr_ice_socket_,entry);
+ nr_ice_socket_destroy(&s1);
+ }
+
+ STAILQ_FOREACH_SAFE(r1, &component->pre_answer_reqs, entry, r2){
+ STAILQ_REMOVE(&component->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
+ nr_ice_pre_answer_request_destroy(&r1);
+ }
+
+ RFREE(component);
+ return(0);
+ }
+
+static int nr_ice_component_create_stun_server_ctx(nr_ice_component *component, nr_ice_socket *isock, nr_socket *sock, nr_transport_addr *addr, char *lufrag, Data *pwd)
+ {
+ char label[256];
+ int r,_status;
+
+ /* Create a STUN server context for this socket */
+ snprintf(label, sizeof(label), "server(%s)", addr->as_string);
+ if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server))
+ ABORT(r);
+ if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
+ ABORT(r);
+
+ /* Add the default STUN credentials so that we can respond before
+ we hear about the peer.*/
+ if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component))
+ ABORT(r);
+
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
+ {
+ nr_socket *sock;
+ nr_ice_socket *isock=0;
+ nr_ice_candidate *cand=0;
+ int i;
+ int j;
+ int r,_status;
+
+ if(ctx->flags & NR_ICE_CTX_FLAGS_ONLY_PROXY) {
+ /* No UDP support if we must use a proxy */
+ return 0;
+ }
+
+ /* Now one ice_socket for each address */
+ for(i=0;i<addr_ct;i++){
+ char suppress;
+
+ if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+ else{
+ if(suppress)
+ continue;
+ }
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string);
+ if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addrs[i].addr,&sock))){
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string);
+ continue;
+ }
+
+ if(r=nr_ice_socket_create(ctx,component,sock,NR_ICE_SOCKET_TYPE_DGRAM,&isock))
+ ABORT(r);
+
+ if (!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) {
+ /* Create one host candidate */
+ if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,0,
+ component->component_id,&cand))
+ ABORT(r);
+
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+
+ /* And a srvrflx candidate for each STUN server */
+ for(j=0;j<ctx->stun_server_ct;j++){
+ /* Skip non-UDP */
+ if(ctx->stun_servers[j].transport!=IPPROTO_UDP)
+ continue;
+
+ /* Ensure id is set (nr_ice_ctx_set_stun_servers does not) */
+ ctx->stun_servers[j].id = j;
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock,sock,SERVER_REFLEXIVE,0,
+ &ctx->stun_servers[j],component->component_id,&cand))
+ ABORT(r);
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+ }
+ }
+ else{
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): relay only option results in no host candidate for %s",ctx->label,addrs[i].addr.as_string);
+ }
+
+#ifdef USE_TURN
+ if ((ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) &&
+ (ctx->turn_server_ct == 0)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): relay only option is set without any TURN server configured",ctx->label);
+ }
+ /* And both a srvrflx and relayed candidate for each TURN server (unless
+ we're in relay-only mode, in which case just the relayed one) */
+ for(j=0;j<ctx->turn_server_ct;j++){
+ nr_socket *turn_sock;
+ nr_ice_candidate *srvflx_cand=0;
+
+ /* Skip non-UDP */
+ if (ctx->turn_servers[j].turn_server.transport != IPPROTO_UDP)
+ continue;
+
+ if (!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) {
+ /* Ensure id is set with a unique value */
+ ctx->turn_servers[j].turn_server.id = j + ctx->stun_server_ct;
+ /* srvrflx */
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock,sock,SERVER_REFLEXIVE,0,
+ &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+ ABORT(r);
+ cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */
+ cand->done_cb=nr_ice_gather_finished_cb;
+ cand->cb_arg=cand;
+
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ srvflx_cand=cand;
+ cand=0;
+ }
+ /* relayed*/
+ if(r=nr_socket_turn_create(sock, &turn_sock))
+ ABORT(r);
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock,turn_sock,RELAYED,0,
+ &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+ ABORT(r);
+ if (srvflx_cand) {
+ cand->u.relayed.srvflx_candidate=srvflx_cand;
+ srvflx_cand->u.srvrflx.relay_candidate=cand;
+ }
+ cand->u.relayed.server=&ctx->turn_servers[j];
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+
+ cand=0;
+ }
+#endif /* USE_TURN */
+
+ /* Create a STUN server context for this socket */
+ if ((r=nr_ice_component_create_stun_server_ctx(component,isock,sock,&addrs[i].addr,lufrag,pwd)))
+ ABORT(r);
+
+ STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
+ }
+
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_component_get_port_from_ephemeral_range(uint16_t *port)
+ {
+ int _status, r;
+ void *buf = port;
+ if(r=nr_crypto_random_bytes(buf, 2))
+ ABORT(r);
+ *port|=49152; /* make it fit into IANA ephemeral port range >= 49152 */
+ _status=0;
+abort:
+ return(_status);
+ }
+
+static int nr_ice_component_create_tcp_host_candidate(struct nr_ice_ctx_ *ctx,
+ nr_ice_component *component, nr_transport_addr *interface_addr, nr_socket_tcp_type tcp_type,
+ int backlog, int so_sock_ct, char *lufrag, Data *pwd, nr_ice_socket **isock)
+ {
+ int r,_status;
+ nr_ice_candidate *cand=0;
+ int tries=3;
+ nr_ice_socket *isock_tmp=0;
+ nr_socket *nrsock=0;
+ nr_transport_addr addr;
+ uint16_t local_port;
+
+ if ((r=nr_transport_addr_copy(&addr,interface_addr)))
+ ABORT(r);
+ addr.protocol=IPPROTO_TCP;
+
+ do{
+ if (!tries--)
+ ABORT(r);
+
+ if((r=nr_ice_component_get_port_from_ephemeral_range(&local_port)))
+ ABORT(r);
+
+ if ((r=nr_transport_addr_set_port(&addr, local_port)))
+ ABORT(r);
+
+ if((r=nr_transport_addr_fmt_addr_string(&addr)))
+ ABORT(r);
+
+ /* It would be better to stop trying if there is error other than
+ port already used, but it'd require significant work to support this. */
+ r=nr_socket_multi_tcp_create(ctx,&addr,tcp_type,so_sock_ct,NR_STUN_MAX_MESSAGE_SIZE,&nrsock);
+
+ } while(r);
+
+ if((tcp_type == TCP_TYPE_PASSIVE) && (r=nr_socket_listen(nrsock,backlog)))
+ ABORT(r);
+
+ if((r=nr_ice_socket_create(ctx,component,nrsock,NR_ICE_SOCKET_TYPE_STREAM_TCP,&isock_tmp)))
+ ABORT(r);
+
+ /* nr_ice_socket took ownership of nrsock */
+ nrsock=NULL;
+
+ /* Create a STUN server context for this socket */
+ if ((r=nr_ice_component_create_stun_server_ctx(component,isock_tmp,isock_tmp->sock,&addr,lufrag,pwd)))
+ ABORT(r);
+
+ if((r=nr_ice_candidate_create(ctx,component,isock_tmp,isock_tmp->sock,HOST,tcp_type,0,
+ component->component_id,&cand)))
+ ABORT(r);
+
+ if (isock)
+ *isock=isock_tmp;
+
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+
+ STAILQ_INSERT_TAIL(&component->sockets,isock_tmp,entry);
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_ice_socket_destroy(&isock_tmp);
+ nr_socket_destroy(&nrsock);
+ }
+ return(_status);
+ }
+
+static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd)
+ {
+ nr_ice_candidate *cand=0;
+ int i;
+ int j;
+ int r,_status;
+ int so_sock_ct=0;
+ int backlog=10;
+ char ice_tcp_disabled=1;
+
+ r_log(LOG_ICE,LOG_DEBUG,"nr_ice_component_initialize_tcp");
+
+ if(r=NR_reg_get_int4(NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT,&so_sock_ct)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+
+ if(r=NR_reg_get_int4(NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG,&backlog)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+
+ if ((r=NR_reg_get_char(NR_ICE_REG_ICE_TCP_DISABLE, &ice_tcp_disabled))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ }
+ if ((ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) ||
+ (ctx->flags & NR_ICE_CTX_FLAGS_ONLY_PROXY)) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): relay/proxy only option results in ICE TCP being disabled",ctx->label);
+ ice_tcp_disabled = 1;
+ }
+
+ for(i=0;i<addr_ct;i++){
+ char suppress;
+ nr_ice_socket *isock_psv=0;
+ nr_ice_socket *isock_so=0;
+
+ if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+ else if(suppress) {
+ continue;
+ }
+
+ if (!ice_tcp_disabled) {
+ /* passive host candidate */
+ if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr,
+ TCP_TYPE_PASSIVE, backlog, 0, lufrag, pwd, &isock_psv))) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to create passive TCP host candidate: %d",ctx->label,r);
+ }
+
+ /* active host candidate */
+ if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr,
+ TCP_TYPE_ACTIVE, 0, 0, lufrag, pwd, NULL))) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to create active TCP host candidate: %d",ctx->label,r);
+ }
+
+ /* simultaneous-open host candidate */
+ if (so_sock_ct) {
+ if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr,
+ TCP_TYPE_SO, 0, so_sock_ct, lufrag, pwd, &isock_so))) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to create simultanous open TCP host candidate: %d",ctx->label,r);
+ }
+ }
+
+ /* And srvrflx candidates for each STUN server */
+ for(j=0;j<ctx->stun_server_ct;j++){
+ if (ctx->stun_servers[j].transport!=IPPROTO_TCP)
+ continue;
+
+ if (isock_psv) {
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock_psv,isock_psv->sock,SERVER_REFLEXIVE,TCP_TYPE_PASSIVE,
+ &ctx->stun_servers[j],component->component_id,&cand))
+ ABORT(r);
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+ }
+
+ if (isock_so) {
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock_so,isock_so->sock,SERVER_REFLEXIVE,TCP_TYPE_SO,
+ &ctx->stun_servers[j],component->component_id,&cand))
+ ABORT(r);
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+ }
+ }
+ }
+
+#ifdef USE_TURN
+ /* Create a new relayed candidate for each addr/TURN server pair */
+ for(j=0;j<ctx->turn_server_ct;j++){
+ nr_transport_addr addr;
+ nr_socket *local_sock;
+ nr_socket *buffered_sock;
+ nr_socket *turn_sock;
+ nr_ice_socket *turn_isock;
+
+ /* Skip non-TCP */
+ if (ctx->turn_servers[j].turn_server.transport != IPPROTO_TCP)
+ continue;
+
+ if (ctx->turn_servers[j].turn_server.type == NR_ICE_STUN_SERVER_TYPE_ADDR &&
+ nr_transport_addr_cmp(&ctx->turn_servers[j].turn_server.u.addr,
+ &addrs[i].addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_VERSION)) {
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): Skipping TURN server because of IP version mis-match (%u - %u)",ctx->label,addrs[i].addr.ip_version,ctx->turn_servers[j].turn_server.u.addr.ip_version);
+ continue;
+ }
+
+ if (!ice_tcp_disabled) {
+ /* Use TURN server to get srflx candidates */
+ if (isock_psv) {
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock_psv,isock_psv->sock,SERVER_REFLEXIVE,TCP_TYPE_PASSIVE,
+ &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+ ABORT(r);
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+ }
+
+ if (isock_so) {
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock_so,isock_so->sock,SERVER_REFLEXIVE,TCP_TYPE_SO,
+ &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+ ABORT(r);
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+ }
+ }
+
+ /* Create relay candidate */
+ if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr)))
+ ABORT(r);
+ addr.protocol = IPPROTO_TCP;
+ if ((r=nr_transport_addr_fmt_addr_string(&addr)))
+ ABORT(r);
+ /* Create a local socket */
+ if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addr,&local_sock))){
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string);
+ continue;
+ }
+
+ r_log(LOG_ICE,LOG_DEBUG,"nr_ice_component_initialize_tcp creating TURN TCP wrappers");
+
+ if (ctx->turn_tcp_socket_wrapper) {
+ /* The HTTP proxy socket */
+ if((r=nr_socket_wrapper_factory_wrap(ctx->turn_tcp_socket_wrapper, local_sock, &local_sock)))
+ ABORT(r);
+ }
+
+ /* The TCP buffered socket */
+ if((r=nr_socket_buffered_stun_create(local_sock, NR_STUN_MAX_MESSAGE_SIZE, TURN_TCP_FRAMING, &buffered_sock)))
+ ABORT(r);
+
+ /* The TURN socket */
+ if(r=nr_socket_turn_create(buffered_sock, &turn_sock))
+ ABORT(r);
+
+ /* Create an ICE socket */
+ if((r=nr_ice_socket_create(ctx, component, buffered_sock, NR_ICE_SOCKET_TYPE_STREAM_TURN, &turn_isock)))
+ ABORT(r);
+
+ /* Attach ourselves to it */
+ if(r=nr_ice_candidate_create(ctx,component,
+ turn_isock,turn_sock,RELAYED,TCP_TYPE_NONE,
+ &ctx->turn_servers[j].turn_server,component->component_id,&cand))
+ ABORT(r);
+ cand->u.relayed.srvflx_candidate=NULL;
+ cand->u.relayed.server=&ctx->turn_servers[j];
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+
+ /* Create a STUN server context for this socket */
+ if ((r=nr_ice_component_create_stun_server_ctx(component,turn_isock,local_sock,&addr,lufrag,pwd)))
+ ABORT(r);
+
+ STAILQ_INSERT_TAIL(&component->sockets,turn_isock,entry);
+ }
+#endif /* USE_TURN */
+ }
+
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+
+/* Make all the candidates we can make at the beginning */
+int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component)
+ {
+ int r,_status;
+ nr_local_addr *addrs=ctx->local_addrs;
+ int addr_ct=ctx->local_addr_ct;
+ char *lufrag;
+ char *lpwd;
+ Data pwd;
+ nr_ice_candidate *cand;
+
+ if (component->candidate_ct) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): component with id %d already has candidates, probably restarting gathering because of a new stream",ctx->label,component->component_id);
+ return(0);
+ }
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id);
+
+ if(addr_ct==0){
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label);
+ ABORT(R_NOT_FOUND);
+ }
+
+ /* Note: we need to recompute these because
+ we have not yet computed the values in the peer media stream.*/
+ lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag;
+ assert(lufrag);
+ if (!lufrag)
+ ABORT(R_INTERNAL);
+ lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
+ assert(lpwd);
+ if (!lpwd)
+ ABORT(R_INTERNAL);
+ INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
+
+ /* Initialize the UDP candidates */
+ if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd))
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create UDP candidates with error %d",ctx->label,r);
+ /* And the TCP candidates */
+ if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd))
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create TCP candidates with error %d",ctx->label,r);
+
+ /* count the candidates that will be initialized */
+ cand=TAILQ_FIRST(&component->candidates);
+ if(!cand){
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): couldn't create any valid candidates",ctx->label);
+ ABORT(R_NOT_FOUND);
+ }
+
+ while(cand){
+ ctx->uninitialized_candidates++;
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+
+ /* Now initialize all the candidates */
+ cand=TAILQ_FIRST(&component->candidates);
+ while(cand){
+ if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){
+ nr_ice_candidate_initialize(cand,nr_ice_gather_finished_cb,cand);
+ }
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_any_peer_paired(nr_ice_candidate* cand) {
+ nr_ice_peer_ctx* pctx=STAILQ_FIRST(&cand->ctx->peers);
+ while(pctx && pctx->state == NR_ICE_PEER_STATE_UNPAIRED){
+ /* Is it worth actually looking through the check lists? Probably not. */
+ pctx=STAILQ_NEXT(pctx,entry);
+ }
+ return pctx != NULL;
+}
+
+/*
+ Compare this newly initialized candidate against the other initialized
+ candidates and discard the lower-priority one if they are redundant.
+
+ This algorithm combined with the other algorithms, favors
+ host > srflx > relay
+ */
+int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned)
+ {
+ nr_ice_candidate *c2, *tmp = NULL;
+
+ *was_pruned = 0;
+ c2 = TAILQ_FIRST(&comp->candidates);
+ while(c2){
+ if((c1 != c2) &&
+ (c2->state == NR_ICE_CAND_STATE_INITIALIZED) &&
+ !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) &&
+ !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){
+
+ if((c1->type == c2->type) ||
+ (!(ctx->flags & NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES) &&
+ ((c1->type==HOST && c2->type == SERVER_REFLEXIVE) ||
+ (c2->type==HOST && c1->type == SERVER_REFLEXIVE)))){
+
+ /*
+ These are redundant. Remove the lower pri one, or if pairing has
+ already occurred, remove the newest one.
+
+ Since this algorithmis run whenever a new candidate
+ is initialized, there should at most one duplicate.
+ */
+ if ((c1->priority <= c2->priority) || nr_ice_any_peer_paired(c2)) {
+ tmp = c1;
+ *was_pruned = 1;
+ }
+ else {
+ tmp = c2;
+ }
+ break;
+ }
+ }
+
+ c2=TAILQ_NEXT(c2,entry_comp);
+ }
+
+ if (tmp) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): Removing redundant candidate",
+ ctx->label,tmp->label);
+
+ TAILQ_REMOVE(&comp->candidates,tmp,entry_comp);
+ comp->candidate_ct--;
+ TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock);
+
+ nr_ice_candidate_destroy(&tmp);
+ }
+
+ return 0;
+ }
+
+static int nr_ice_component_pair_matches_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_transport_addr *local_addr, nr_stun_server_request *req)
+ {
+ if(pair->remote->component->component_id!=comp->component_id)
+ return(0);
+
+ if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ return(0);
+
+ if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ return(0);
+
+ return(1);
+ }
+
+static int nr_ice_component_handle_triggered_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_stun_server_request *req, int *error)
+ {
+ nr_stun_message *sreq=req->request;
+ int r=0,_status;
+
+ if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){
+ if(comp->stream->pctx->controlling){
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword);
+ }
+ else{
+ /* If this is the first time we've noticed this is nominated...*/
+ pair->peer_nominated=1;
+
+ if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){
+ pair->nominated=1;
+
+ if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) {
+ *error=(r==R_NO_MEMORY)?500:400;
+ ABORT(r);
+ }
+ }
+ }
+ }
+
+ /* Note: the RFC says to trigger first and then nominate. But in that case
+ * the canceled trigger pair would get nominated and the cloned trigger pair
+ * would not get the nomination status cloned with it.*/
+ if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) {
+ *error=(r==R_NO_MEMORY)?500:400;
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(r);
+ }
+
+/* Section 7.2.1 */
+static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error)
+ {
+ nr_ice_cand_pair *pair;
+ nr_ice_candidate *pcand=0;
+ nr_stun_message *sreq=req->request;
+ nr_stun_message_attribute *attr;
+ int r=0,_status;
+ int found_valid=0;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string);
+
+ if (comp->state == NR_ICE_COMPONENT_DISABLED)
+ ABORT(R_REJECTED);
+
+ /* Check for role conficts (7.2.1.1) */
+ if(comp->stream->pctx->controlling){
+ if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLING,&attr)){
+ /* OK, there is a conflict. Who's right? */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlling",comp->stream->pctx->label);
+
+ if(attr->u.ice_controlling > comp->stream->pctx->tiebreaker){
+ /* Update the peer ctx. This will propagate to all candidate pairs
+ in the context. */
+ nr_ice_peer_ctx_switch_controlling_role(comp->stream->pctx);
+ }
+ else {
+ /* We are: throw an error */
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label);
+
+ *error=487;
+ ABORT(R_REJECTED);
+ }
+ }
+ }
+ else{
+ if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLED,&attr)){
+ /* OK, there is a conflict. Who's right? */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlled",comp->stream->pctx->label);
+
+ if(attr->u.ice_controlled < comp->stream->pctx->tiebreaker){
+ /* Update the peer ctx. This will propagate to all candidate pairs
+ in the context. */
+ nr_ice_peer_ctx_switch_controlling_role(comp->stream->pctx);
+ }
+ else {
+ /* We are: throw an error */
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label);
+
+ *error=487;
+ ABORT(R_REJECTED);
+ }
+ }
+ }
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): This STUN request appears to map to local addr %s",comp->stream->pctx->label,local_addr->as_string);
+
+ pair=TAILQ_FIRST(&comp->stream->check_list);
+ while(pair){
+ /* Since triggered checks create duplicate pairs (in this implementation)
+ * we are willing to handle multiple matches here. */
+ if(nr_ice_component_pair_matches_check(comp, pair, local_addr, req)){
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s",comp->stream->pctx->label,pair->codeword,pair->as_string);
+ if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error))
+ ABORT(r);
+ ++found_valid;
+ }
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+
+ if(!found_valid){
+ /* There were no matching pairs, so we need to create a new peer
+ * reflexive candidate pair. */
+
+ if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label);
+ *error=400;
+ ABORT(R_BAD_DATA);
+ }
+
+ /* Find our local component candidate */
+ nr_ice_candidate *cand;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label);
+ cand=TAILQ_FIRST(&comp->local_component->candidates);
+ while(cand){
+ if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ break;
+
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+
+ /* Well, this really shouldn't happen, but it's an error from the
+ other side, so we just throw an error and keep going */
+ if(!cand){
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string);
+
+ *error=400;
+ ABORT(R_NOT_FOUND);
+ }
+
+ /* Now make a peer reflexive (remote) candidate */
+ if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) {
+ *error=(r==R_NO_MEMORY)?500:400;
+ ABORT(r);
+ }
+ pcand->priority=attr->u.priority;
+ pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
+
+ /* Finally, create the candidate pair, insert into the check list, and
+ * apply the incoming check to it. */
+ if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand,
+ &pair)) {
+ *error=(r==R_NO_MEMORY)?500:400;
+ ABORT(r);
+ }
+
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
+ if(r=nr_ice_component_insert_pair(comp,pair)) {
+ *error=(r==R_NO_MEMORY)?500:400;
+ nr_ice_candidate_pair_destroy(&pair);
+ ABORT(r);
+ }
+
+ /* Do this last, since any call to ABORT will destroy pcand */
+ TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp);
+ pcand=0;
+
+ /* Finally start the trigger check if needed */
+ if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ if(_status){
+ nr_ice_candidate_destroy(&pcand);
+ assert(*error != 0);
+ if(r!=R_NO_MEMORY) assert(*error != 500);
+ }
+ return(_status);
+ }
+
+static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error)
+ {
+ nr_ice_component *comp=cb_arg;
+ nr_transport_addr local_addr;
+ int r,_status;
+
+ if(comp->state==NR_ICE_COMPONENT_FAILED) {
+ *error=400;
+ ABORT(R_REJECTED);
+ }
+
+ /* Find the candidate pair that this maps to */
+ if(r=nr_socket_getaddr(sock,&local_addr)) {
+ *error=500;
+ ABORT(r);
+ }
+
+ if (r=nr_ice_component_process_incoming_check(comp, &local_addr, req, error))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced)
+ {
+ nr_ice_pre_answer_request *r1,*r2;
+ nr_ice_component *comp = pcomp->local_component;
+ int r,_status;
+
+ if (serviced)
+ *serviced = 0;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id);
+
+ STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) {
+ if (!strcmp(r1->username, username)) {
+ int error = 0;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id);
+ r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error);
+ if (r) {
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error);
+ }
+ (*serviced)++;
+ STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry);
+ nr_ice_pre_answer_request_destroy(&r1);
+ }
+ }
+
+ _status=0;
+ return(_status);
+ }
+
+int nr_ice_component_can_candidate_tcptype_pair(nr_socket_tcp_type left, nr_socket_tcp_type right)
+ {
+ if (left && !right)
+ return(0);
+ if (!left && right)
+ return(0);
+ if (left == TCP_TYPE_ACTIVE && right != TCP_TYPE_PASSIVE)
+ return(0);
+ if (left == TCP_TYPE_SO && right != TCP_TYPE_SO)
+ return(0);
+ if (left == TCP_TYPE_PASSIVE)
+ return(0);
+
+ return(1);
+ }
+
+/* filter out pairings which won't work. */
+int nr_ice_component_can_candidate_addr_pair(nr_transport_addr *local, nr_transport_addr *remote)
+ {
+ if(local->ip_version != remote->ip_version)
+ return(0);
+ if(nr_transport_addr_is_link_local(local) !=
+ nr_transport_addr_is_link_local(remote))
+ return(0);
+ /* This prevents our ice_unittest (or broken clients) from pairing a
+ * loopback with a host candidate. */
+ if(nr_transport_addr_is_loopback(local) !=
+ nr_transport_addr_is_loopback(remote))
+ return(0);
+
+ return(1);
+ }
+
+int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
+ {
+ int r, _status;
+ nr_ice_candidate *pcand;
+ nr_ice_cand_pair *pair=0;
+ char codeword[5];
+
+ nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword);
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing local candidate %s",pctx->label,codeword,lcand->label);
+
+ switch(lcand->type){
+ case HOST:
+ break;
+ case SERVER_REFLEXIVE:
+ case PEER_REFLEXIVE:
+ /* Don't actually pair these candidates */
+ goto done;
+ break;
+ case RELAYED:
+ break;
+ default:
+ assert(0);
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ TAILQ_FOREACH(pcand, &pcomp->candidates, entry_comp){
+ if(!nr_ice_component_can_candidate_addr_pair(&lcand->addr, &pcand->addr))
+ continue;
+ if(!nr_ice_component_can_candidate_tcptype_pair(lcand->tcp_type, pcand->tcp_type))
+ continue;
+
+ /*
+ Two modes, depending on |pair_all_remote|
+
+ 1. Pair remote candidates which have not been paired
+ (used in initial pairing or in processing the other side's
+ trickle candidates).
+ 2. Pair any remote candidate (used when processing our own
+ trickle candidates).
+ */
+ if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) {
+ if (pair_all_remote) {
+ /* When a remote candidate arrives after the start of checking, but
+ * before the gathering of local candidates, it can be in UNPAIRED */
+ pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
+ }
+
+ nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword);
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s", pctx->label, codeword, pcand->label);
+
+ if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair))
+ ABORT(r);
+
+ if(r=nr_ice_component_insert_pair(pcomp, pair))
+ ABORT(r);
+ }
+ }
+
+ done:
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp)
+ {
+ nr_ice_candidate *lcand, *pcand;
+ nr_ice_socket *isock;
+ int r,_status;
+
+ r_log(LOG_ICE,LOG_DEBUG,"Pairing candidates======");
+
+ /* Create the candidate pairs */
+ lcand=TAILQ_FIRST(&lcomp->candidates);
+
+ if (!lcand) {
+ /* No local candidates, initialized or not! */
+ ABORT(R_FAILED);
+ }
+
+ while(lcand){
+ if (lcand->state == NR_ICE_CAND_STATE_INITIALIZED) {
+ if ((r = nr_ice_component_pair_candidate(pctx, pcomp, lcand, 0)))
+ ABORT(r);
+ }
+
+ lcand=TAILQ_NEXT(lcand,entry_comp);
+ }
+
+ /* Mark all peer candidates as paired */
+ pcand=TAILQ_FIRST(&pcomp->candidates);
+ while(pcand){
+ pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
+
+ pcand=TAILQ_NEXT(pcand,entry_comp);
+
+ }
+
+ /* Now register the STUN server callback for this component.
+ Note that this is a per-component CB so we only need to
+ do this once.
+ */
+ if (pcomp->state != NR_ICE_COMPONENT_RUNNING) {
+ isock=STAILQ_FIRST(&lcomp->sockets);
+ while(isock){
+ if(r=nr_stun_server_add_client(isock->stun_server,pctx->label,
+ pcomp->stream->r2l_user,&pcomp->stream->r2l_pass,nr_ice_component_stun_server_cb,pcomp)) {
+ ABORT(r);
+ }
+ isock=STAILQ_NEXT(isock,entry);
+ }
+ }
+
+ pcomp->state = NR_ICE_COMPONENT_RUNNING;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_pre_answer_enqueue(nr_ice_component *comp, nr_socket *sock, nr_stun_server_request *req, int *dont_free)
+ {
+ int r = 0;
+ int _status;
+ nr_ice_pre_answer_request *r1, *r2;
+ nr_transport_addr dst_addr;
+ nr_ice_pre_answer_request *par = 0;
+
+ if (r=nr_socket_getaddr(sock, &dst_addr))
+ ABORT(r);
+
+ STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) {
+ if (!nr_transport_addr_cmp(&r1->local_addr, &dst_addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL) &&
+ !nr_transport_addr_cmp(&r1->req.src_addr, &req->src_addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ return(0);
+ }
+ }
+
+ if (r=nr_ice_pre_answer_request_create(&dst_addr, req, &par))
+ ABORT(r);
+
+ r_log(LOG_ICE,LOG_DEBUG, "ICE(%s)/STREAM(%s)/COMP(%d): Enqueuing STUN request pre-answer from %s",
+ comp->ctx->label, comp->stream->label, comp->component_id,
+ req->src_addr.as_string);
+
+ *dont_free = 1;
+ STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry);
+
+ _status=0;
+abort:
+ return(_status);
+ }
+
+/* Fires when we have an incoming candidate that doesn't correspond to an existing
+ remote peer. This is either pre-answer or just spurious. Store it in the
+ component for use when we see the actual answer, at which point we need
+ to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb.
+ */
+static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error)
+ {
+ int r, _status;
+ nr_ice_component *comp = (nr_ice_component *)cb_arg;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s",
+ comp->ctx->label, comp->stream->label, comp->component_id,
+ req->src_addr.as_string);
+
+ if (r=nr_ice_pre_answer_enqueue(comp, sock, req, dont_free)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Failed (%d) to enque pre-answer request from %s",
+ comp->ctx->label, comp->stream->label, comp->component_id, r,
+ req->src_addr.as_string);
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+#define NR_ICE_CONSENT_TIMER_DEFAULT 5000
+#define NR_ICE_CONSENT_TIMEOUT_DEFAULT 30000
+
+static void nr_ice_component_consent_failed(nr_ice_component *comp)
+ {
+ if (!comp->can_send) {
+ return;
+ }
+
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh failed",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ comp->can_send = 0;
+
+ if (comp->consent_timeout) {
+ NR_async_timer_cancel(comp->consent_timeout);
+ comp->consent_timeout = 0;
+ }
+ if (comp->consent_timer) {
+ NR_async_timer_cancel(comp->consent_timer);
+ comp->consent_timer = 0;
+ }
+ /* We are turning the consent failure into a ICE component failure to
+ * alert the browser via ICE connection state change about this event. */
+ if (nr_ice_media_stream_component_failed(comp->stream, comp))
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): failed to mark component as failed",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ }
+
+static void nr_ice_component_consent_timeout_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_component *comp=cb_arg;
+
+ comp->consent_timeout = 0;
+
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh final time out",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ nr_ice_component_consent_failed(comp);
+ }
+
+
+static void nr_ice_component_consent_request_timed_out(nr_ice_component *comp)
+ {
+ if (!comp->can_send) {
+ return;
+ }
+
+ nr_ice_peer_ctx_disconnected(comp->stream->pctx);
+ }
+
+static void nr_ice_component_consent_refreshed(nr_ice_component *comp)
+ {
+ uint16_t tval;
+
+ if (!comp->can_send) {
+ return;
+ }
+
+ gettimeofday(&comp->consent_last_seen, 0);
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): consent_last_seen is now %lu",
+ comp->ctx->label, comp->stream->label, comp->component_id,
+ comp->consent_last_seen.tv_sec);
+
+ nr_ice_peer_ctx_connected(comp->stream->pctx);
+
+ if (comp->consent_timeout)
+ NR_async_timer_cancel(comp->consent_timeout);
+
+ tval = NR_ICE_CONSENT_TIMEOUT_DEFAULT;
+ if (comp->ctx->test_timer_divider)
+ tval = tval / comp->ctx->test_timer_divider;
+
+ NR_ASYNC_TIMER_SET(tval, nr_ice_component_consent_timeout_cb, comp,
+ &comp->consent_timeout);
+ }
+
+static void nr_ice_component_refresh_consent_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_component *comp=cb_arg;
+
+ switch (comp->consent_ctx->state) {
+ case NR_STUN_CLIENT_STATE_FAILED:
+ if (comp->consent_ctx->error_code == 403) {
+ r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent revoked by peer",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ nr_ice_component_consent_failed(comp);
+ }
+ break;
+ case NR_STUN_CLIENT_STATE_DONE:
+ r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent refreshed",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ nr_ice_component_consent_refreshed(comp);
+ break;
+ case NR_STUN_CLIENT_STATE_TIMED_OUT:
+ r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): A single consent refresh request timed out",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ nr_ice_component_consent_request_timed_out(comp);
+ break;
+ default:
+ break;
+ }
+ }
+
+int nr_ice_component_refresh_consent(nr_stun_client_ctx *ctx, NR_async_cb finished_cb, void *cb_arg)
+ {
+ int r,_status;
+
+ nr_stun_client_reset(ctx);
+
+ if (r=nr_stun_client_start(ctx, NR_ICE_CLIENT_MODE_BINDING_REQUEST, finished_cb, cb_arg))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+void nr_ice_component_consent_calc_consent_timer(nr_ice_component *comp)
+ {
+ uint16_t trange, trand, tval;
+
+ trange = NR_ICE_CONSENT_TIMER_DEFAULT * 20 / 100;
+ tval = NR_ICE_CONSENT_TIMER_DEFAULT - trange;
+ if (!nr_crypto_random_bytes((UCHAR*)&trand, sizeof(trand)))
+ tval += (trand % (trange * 2));
+
+ if (comp->ctx->test_timer_divider)
+ tval = tval / comp->ctx->test_timer_divider;
+
+ /* The timeout of the transaction is the maximum time until we send the
+ * next consent request. */
+ comp->consent_ctx->maximum_transmits_timeout_ms = tval;
+ }
+
+static void nr_ice_component_consent_timer_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_component *comp=cb_arg;
+ int r;
+
+ comp->consent_timer = 0;
+
+ comp->consent_ctx->params.ice_binding_request.username =
+ comp->stream->l2r_user;
+ comp->consent_ctx->params.ice_binding_request.password =
+ comp->stream->l2r_pass;
+ comp->consent_ctx->params.ice_binding_request.control =
+ comp->stream->pctx->controlling?
+ NR_ICE_CONTROLLING:NR_ICE_CONTROLLED;
+ comp->consent_ctx->params.ice_binding_request.tiebreaker =
+ comp->stream->pctx->tiebreaker;
+ comp->consent_ctx->params.ice_binding_request.priority =
+ comp->active->local->priority;
+
+ nr_ice_component_consent_calc_consent_timer(comp);
+
+ if (r=nr_ice_component_refresh_consent(comp->consent_ctx,
+ nr_ice_component_refresh_consent_cb,
+ comp)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Refresh consent failed with %d",
+ comp->ctx->label, comp->stream->label, comp->component_id, r);
+ /* In case our attempt to send the refresh binding request reports an
+ * error we don't have to wait for timeouts, but declare this connection
+ * dead right away. */
+ if (r != R_WOULDBLOCK) {
+ nr_ice_component_consent_failed(comp);
+ }
+ }
+
+ nr_ice_component_consent_schedule_consent_timer(comp);
+
+ }
+
+void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp)
+ {
+ if (!comp->can_send) {
+ return;
+ }
+
+ NR_ASYNC_TIMER_SET(comp->consent_ctx->maximum_transmits_timeout_ms,
+ nr_ice_component_consent_timer_cb, comp,
+ &comp->consent_timer);
+ }
+
+void nr_ice_component_consent_destroy(nr_ice_component *comp)
+ {
+ if (comp->consent_timer) {
+ NR_async_timer_cancel(comp->consent_timer);
+ comp->consent_timer = 0;
+ }
+ if (comp->consent_timeout) {
+ NR_async_timer_cancel(comp->consent_timeout);
+ comp->consent_timeout = 0;
+ }
+ if (comp->consent_handle) {
+ nr_ice_socket_deregister(comp->active->local->isock,
+ comp->consent_handle);
+ comp->consent_handle = 0;
+ }
+ if (comp->consent_ctx) {
+ nr_stun_client_ctx_destroy(&comp->consent_ctx);
+ }
+ }
+
+int nr_ice_component_setup_consent(nr_ice_component *comp)
+ {
+ int r,_status;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Setting up refresh consent",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+
+ if (r=nr_stun_client_ctx_create("consent", comp->active->local->osock,
+ &comp->active->remote->addr, 0,
+ &comp->consent_ctx))
+ ABORT(r);
+ /* Consent request get send only once. */
+ comp->consent_ctx->maximum_transmits = 1;
+
+ if (r=nr_ice_socket_register_stun_client(comp->active->local->isock,
+ comp->consent_ctx, &comp->consent_handle))
+ ABORT(r);
+
+ comp->can_send = 1;
+ nr_ice_component_consent_refreshed(comp);
+
+ nr_ice_component_consent_calc_consent_timer(comp);
+ nr_ice_component_consent_schedule_consent_timer(comp);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+ nr_ice_cand_pair *p2;
+
+ /* Are we changing what the nominated pair is? */
+ if(comp->nominated){
+ if(comp->nominated->priority >= pair->priority)
+ return(0);
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s)",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->codeword,comp->nominated->as_string,pair->codeword);
+ /* As consent doesn't hold a reference to its isock this needs to happen
+ * before making the new pair the active one. */
+ nr_ice_component_consent_destroy(comp);
+ }
+
+ /* Set the new nominated pair */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): nominated pair is %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string);
+ comp->state=NR_ICE_COMPONENT_NOMINATED;
+ comp->nominated=pair;
+ comp->active=pair;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string);
+
+ /* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */
+ p2=TAILQ_FIRST(&comp->stream->trigger_check_queue);
+ while(p2){
+ if((p2 != pair) &&
+ (p2->remote->component->component_id == comp->component_id)) {
+ assert(p2->state == NR_ICE_PAIR_STATE_WAITING ||
+ p2->state == NR_ICE_PAIR_STATE_CANCELLED);
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s in trigger check queue because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword);
+
+ if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2,0))
+ ABORT(r);
+ }
+
+ p2=TAILQ_NEXT(p2,triggered_check_queue_entry);
+ }
+ p2=TAILQ_FIRST(&comp->stream->check_list);
+ while(p2){
+ if((p2 != pair) &&
+ (p2->remote->component->component_id == comp->component_id) &&
+ ((p2->state == NR_ICE_PAIR_STATE_FROZEN) ||
+ (p2->state == NR_ICE_PAIR_STATE_WAITING))) {
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword);
+
+ if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2,0))
+ ABORT(r);
+ }
+
+ p2=TAILQ_NEXT(p2,check_queue_entry);
+ }
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id);
+
+ if(r=nr_ice_component_setup_consent(comp))
+ ABORT(r);
+
+ if(r=nr_ice_media_stream_component_nominated(comp->stream,comp))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_component_have_all_pairs_failed(nr_ice_component *comp)
+ {
+ nr_ice_cand_pair *p2;
+
+ p2=TAILQ_FIRST(&comp->stream->check_list);
+ while(p2){
+ if(comp->component_id==p2->local->component_id){
+ switch(p2->state){
+ case NR_ICE_PAIR_STATE_FROZEN:
+ case NR_ICE_PAIR_STATE_WAITING:
+ case NR_ICE_PAIR_STATE_IN_PROGRESS:
+ case NR_ICE_PAIR_STATE_SUCCEEDED:
+ return(0);
+ case NR_ICE_PAIR_STATE_FAILED:
+ case NR_ICE_PAIR_STATE_CANCELLED:
+ /* states that will never be recovered from */
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ p2=TAILQ_NEXT(p2,check_queue_entry);
+ }
+
+ return(1);
+ }
+
+int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
+ {
+ return nr_ice_component_check_if_failed(comp);
+ }
+
+int nr_ice_component_check_if_failed(nr_ice_component *comp)
+ {
+ if (comp->state == NR_ICE_COMPONENT_RUNNING) {
+ /* Don't do anything to streams that aren't currently running */
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): Checking whether component needs to be marked failed.",comp->stream->pctx->label,comp->stream->label,comp->component_id);
+
+ if (!comp->stream->pctx->trickle_grace_period_timer &&
+ nr_ice_component_have_all_pairs_failed(comp)) {
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): All pairs are failed, and grace period has elapsed. Marking component as failed.",comp->stream->pctx->label,comp->stream->label,comp->component_id);
+ return nr_ice_media_stream_component_failed(comp->stream,comp);
+ }
+ }
+
+ return(0);
+ }
+
+int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp)
+ {
+ nr_ice_cand_pair **pairs=0;
+ int ct=0;
+ nr_ice_cand_pair *pair;
+ int r,_status;
+
+ /* Size the array */
+ pair=TAILQ_FIRST(&comp->stream->check_list);
+ while(pair){
+ if (comp->component_id == pair->local->component_id)
+ ct++;
+
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+
+ /* Make and fill the array */
+ if(!(pairs=RCALLOC(sizeof(nr_ice_cand_pair *)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ ct=0;
+ pair=TAILQ_FIRST(&comp->stream->check_list);
+ while(pair){
+ if (comp->component_id == pair->local->component_id)
+ pairs[ct++]=pair;
+
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+
+ if (pctx->handler) {
+ if(r=pctx->handler->vtbl->select_pair(pctx->handler->obj,
+ comp->stream,comp->component_id,pairs,ct))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ RFREE(pairs);
+ return(_status);
+ }
+
+
+/* Close the underlying sockets for everything but the nominated candidate */
+int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp)
+ {
+ nr_ice_socket *isock=0;
+ nr_ice_socket *s1,*s2;
+
+ if(rcomp->state==NR_ICE_COMPONENT_NOMINATED){
+ assert(rcomp->active == rcomp->nominated);
+ isock=rcomp->nominated->local->isock;
+ }
+
+ STAILQ_FOREACH_SAFE(s1, &lcomp->sockets, entry, s2){
+ if(s1!=isock){
+ STAILQ_REMOVE(&lcomp->sockets,s1,nr_ice_socket_,entry);
+ nr_ice_socket_destroy(&s1);
+ }
+ }
+
+ return(0);
+ }
+
+
+int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+
+ /* Pairs for peer reflexive are marked SUCCEEDED immediately */
+ if (pair->state != NR_ICE_PAIR_STATE_FROZEN &&
+ pair->state != NR_ICE_PAIR_STATE_SUCCEEDED){
+ assert(0);
+ ABORT(R_BAD_ARGS);
+ }
+
+ if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,pair))
+ ABORT(r);
+
+ /* Make sure the check timer is running, if the stream was previously
+ * started. We will not start streams just because a pair was created,
+ * unless it is the first pair to be created across all streams. */
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): Ensure that check timer is running for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string);
+
+ if(pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE ||
+ (pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN &&
+ !pair->remote->stream->pctx->checks_started)){
+ if(nr_ice_media_stream_start_checks(pair->remote->stream->pctx, pair->remote->stream)) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Could not restart checks for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string);
+ ABORT(R_INTERNAL);
+ }
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version)
+ {
+ int _status;
+ nr_ice_candidate *cand;
+ nr_ice_candidate *best_cand = NULL;
+
+ /* We have the component. Now find the "best" candidate, making
+ use of the fact that more "reliable" candidate types have
+ higher numbers. So, we sort by type and then priority within
+ type
+ */
+ cand=TAILQ_FIRST(&comp->candidates);
+ while(cand){
+ if (!nr_ice_ctx_hide_candidate(comp->ctx, cand) &&
+ cand->addr.ip_version == ip_version) {
+ if (!best_cand) {
+ best_cand = cand;
+ }
+ else if (best_cand->type < cand->type) {
+ best_cand = cand;
+ } else if (best_cand->type == cand->type &&
+ best_cand->priority < cand->priority) {
+ best_cand = cand;
+ }
+ }
+
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+
+ /* No candidates */
+ if (!best_cand)
+ ABORT(R_NOT_FOUND);
+
+ *candp = best_cand;
+
+ _status=0;
+ abort:
+ return(_status);
+
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_component.h b/media/mtransport/third_party/nICEr/src/ice/ice_component.h
new file mode 100644
index 000000000..5bb0ea1e7
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.h
@@ -0,0 +1,104 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_component_h
+#define _ice_component_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct nr_ice_pre_answer_request_ {
+ nr_stun_server_request req;
+ char *username;
+ nr_transport_addr local_addr;
+
+ STAILQ_ENTRY(nr_ice_pre_answer_request_) entry;
+} nr_ice_pre_answer_request;
+
+typedef STAILQ_HEAD(nr_ice_pre_answer_request_head_, nr_ice_pre_answer_request_) nr_ice_pre_answer_request_head;
+
+struct nr_ice_component_ {
+ int state;
+#define NR_ICE_COMPONENT_UNPAIRED 0
+#define NR_ICE_COMPONENT_RUNNING 1
+#define NR_ICE_COMPONENT_NOMINATED 2
+#define NR_ICE_COMPONENT_FAILED 3
+#define NR_ICE_COMPONENT_DISABLED 4
+ struct nr_ice_ctx_ *ctx;
+ struct nr_ice_media_stream_ *stream;
+ nr_ice_component *local_component;
+
+ int component_id;
+ nr_ice_socket_head sockets;
+ nr_ice_candidate_head candidates;
+ int candidate_ct;
+ nr_ice_pre_answer_request_head pre_answer_reqs;
+
+ int valid_pairs;
+ struct nr_ice_cand_pair_ *nominated; /* Highest priority nomninated pair */
+ struct nr_ice_cand_pair_ *active;
+
+ nr_stun_client_ctx *consent_ctx;
+ void *consent_timer;
+ void *consent_timeout;
+ void *consent_handle;
+ int can_send;
+ struct timeval consent_last_seen;
+
+ STAILQ_ENTRY(nr_ice_component_)entry;
+};
+
+typedef STAILQ_HEAD(nr_ice_component_head_,nr_ice_component_) nr_ice_component_head;
+
+int nr_ice_component_create(struct nr_ice_media_stream_ *stream, int component_id, nr_ice_component **componentp);
+int nr_ice_component_destroy(nr_ice_component **componentp);
+int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component);
+int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned);
+int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote);
+int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp, nr_ice_component *pcomp);
+int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced);
+int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
+int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
+int nr_ice_component_check_if_failed(nr_ice_component *comp);
+int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp);
+int nr_ice_component_set_failed(nr_ice_component *comp);
+int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp);
+int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair);
+int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
new file mode 100644
index 000000000..08810a92d
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
@@ -0,0 +1,1057 @@
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <csi_platform.h>
+#include <assert.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/queue.h>
+#include <string.h>
+#include <nr_api.h>
+#include <registry.h>
+#include "stun.h"
+#include "ice_ctx.h"
+#include "ice_reg.h"
+#include "nr_crypto.h"
+#include "async_timer.h"
+#include "util.h"
+#include "nr_socket_local.h"
+
+#define ICE_UFRAG_LEN 8
+#define ICE_PWD_LEN 32
+
+int LOG_ICE = 0;
+
+static int nr_ice_random_string(char *str, int len);
+static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out);
+#ifdef USE_TURN
+static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out);
+#endif /* USE_TURN */
+static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg);
+static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand);
+static int no_op(void **obj) {
+ return 0;
+}
+
+static nr_socket_factory_vtbl default_socket_factory_vtbl = {
+ nr_socket_local_create,
+ no_op
+};
+
+int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out)
+ {
+ int r,_status;
+ nr_ice_stun_server *servers = 0;
+ int i;
+ NR_registry child;
+ char *addr=0;
+ UINT2 port;
+ in_addr_t addr_int;
+
+ if(!(servers=RCALLOC(sizeof(nr_ice_stun_server)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ for(i=0;i<ct;i++){
+ if(r=NR_reg_get_child_registry(NR_ICE_REG_STUN_SRV_PRFX,i,child))
+ ABORT(r);
+ /* Assume we have a v4 addr for now */
+ if(r=NR_reg_alloc2_string(child,"addr",&addr))
+ ABORT(r);
+ addr_int=inet_addr(addr);
+ if(addr_int==INADDR_NONE){
+ r_log(LOG_ICE,LOG_ERR,"Invalid address %s;",addr);
+ ABORT(R_BAD_ARGS);
+ }
+ if(r=NR_reg_get2_uint2(child,"port",&port)) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ port = 3478;
+ }
+ if(r=nr_ip4_port_to_transport_addr(ntohl(addr_int), port, IPPROTO_UDP,
+ &servers[i].u.addr))
+ ABORT(r);
+ servers[i].type = NR_ICE_STUN_SERVER_TYPE_ADDR;
+ RFREE(addr);
+ addr=0;
+ }
+
+ *out = servers;
+
+ _status=0;
+ abort:
+ RFREE(addr);
+ if (_status) RFREE(servers);
+ return(_status);
+ }
+
+int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers,int ct)
+ {
+ int _status;
+
+ if(ctx->stun_servers){
+ RFREE(ctx->stun_servers);
+ ctx->stun_server_ct=0;
+ }
+
+ if (ct) {
+ if(!(ctx->stun_servers=RCALLOC(sizeof(nr_ice_stun_server)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ memcpy(ctx->stun_servers,servers,sizeof(nr_ice_stun_server)*ct);
+ ctx->stun_server_ct = ct;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers,int ct)
+ {
+ int _status;
+
+ if(ctx->turn_servers){
+ RFREE(ctx->turn_servers);
+ ctx->turn_server_ct=0;
+ }
+
+ if(ct) {
+ if(!(ctx->turn_servers=RCALLOC(sizeof(nr_ice_turn_server)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ memcpy(ctx->turn_servers,servers,sizeof(nr_ice_turn_server)*ct);
+ ctx->turn_server_ct = ct;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_ctx_copy_turn_servers(nr_ice_ctx *ctx, nr_ice_turn_server *servers, int ct)
+ {
+ int _status, i, r;
+
+ if (r = nr_ice_ctx_set_turn_servers(ctx, servers, ct)) {
+ ABORT(r);
+ }
+
+ // make copies of the username and password so they aren't freed twice
+ for (i = 0; i < ct; ++i) {
+ if (!(ctx->turn_servers[i].username = r_strdup(servers[i].username))) {
+ ABORT(R_NO_MEMORY);
+ }
+ if (r = r_data_create(&ctx->turn_servers[i].password,
+ servers[i].password->data,
+ servers[i].password->len)) {
+ ABORT(r);
+ }
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_ctx_set_local_addrs(nr_ice_ctx *ctx,nr_local_addr *addrs,int ct)
+ {
+ int _status,i,r;
+
+ if(ctx->local_addrs) {
+ RFREE(ctx->local_addrs);
+ ctx->local_addr_ct=0;
+ ctx->local_addrs=0;
+ }
+
+ if (ct) {
+ if(!(ctx->local_addrs=RCALLOC(sizeof(nr_local_addr)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ for (i=0;i<ct;++i) {
+ if (r=nr_local_addr_copy(ctx->local_addrs+i,addrs+i)) {
+ ABORT(r);
+ }
+ }
+ ctx->local_addr_ct = ct;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver)
+ {
+ int _status;
+
+ if (ctx->resolver) {
+ ABORT(R_ALREADY);
+ }
+
+ ctx->resolver = resolver;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *ip)
+ {
+ int _status;
+
+ if (ctx->interface_prioritizer) {
+ ABORT(R_ALREADY);
+ }
+
+ ctx->interface_prioritizer = ip;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper)
+ {
+ int _status;
+
+ if (ctx->turn_tcp_socket_wrapper) {
+ ABORT(R_ALREADY);
+ }
+
+ ctx->turn_tcp_socket_wrapper = wrapper;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory)
+ {
+ nr_socket_factory_destroy(&ctx->socket_factory);
+ ctx->socket_factory = factory;
+ }
+
+#ifdef USE_TURN
+int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out)
+ {
+ int r,_status;
+ nr_ice_turn_server *servers = 0;
+ int i;
+ NR_registry child;
+ char *addr=0;
+ UINT2 port;
+ in_addr_t addr_int;
+ Data data={0};
+
+ if(!(servers=RCALLOC(sizeof(nr_ice_turn_server)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ for(i=0;i<ct;i++){
+ if(r=NR_reg_get_child_registry(NR_ICE_REG_TURN_SRV_PRFX,i,child))
+ ABORT(r);
+ /* Assume we have a v4 addr for now */
+ if(r=NR_reg_alloc2_string(child,"addr",&addr))
+ ABORT(r);
+ addr_int=inet_addr(addr);
+ if(addr_int==INADDR_NONE){
+ r_log(LOG_ICE,LOG_ERR,"Invalid address %s",addr);
+ ABORT(R_BAD_ARGS);
+ }
+ if(r=NR_reg_get2_uint2(child,"port",&port)) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ port = 3478;
+ }
+ if(r=nr_ip4_port_to_transport_addr(ntohl(addr_int), port, IPPROTO_UDP,
+ &servers[i].turn_server.u.addr))
+ ABORT(r);
+
+
+ if(r=NR_reg_alloc2_string(child,NR_ICE_REG_TURN_SRV_USERNAME,&servers[i].username)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+
+ if(r=NR_reg_alloc2_data(child,NR_ICE_REG_TURN_SRV_PASSWORD,&data)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+ else {
+ servers[i].password=RCALLOC(sizeof(*servers[i].password));
+ if(!servers[i].password)
+ ABORT(R_NO_MEMORY);
+ servers[i].password->data = data.data;
+ servers[i].password->len = data.len;
+ data.data=0;
+ }
+
+ RFREE(addr);
+ addr=0;
+ }
+
+ *out = servers;
+
+ _status=0;
+ abort:
+ RFREE(data.data);
+ RFREE(addr);
+ if (_status) RFREE(servers);
+ return(_status);
+ }
+#endif /* USE_TURN */
+
+#define MAXADDRS 100 /* Ridiculously high */
+int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp)
+ {
+ int r,_status;
+ char *ufrag = 0;
+ char *pwd = 0;
+
+ if (r=nr_ice_get_new_ice_ufrag(&ufrag))
+ ABORT(r);
+ if (r=nr_ice_get_new_ice_pwd(&pwd))
+ ABORT(r);
+
+ if (r=nr_ice_ctx_create_with_credentials(label, flags, ufrag, pwd, ctxp))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ RFREE(ufrag);
+ RFREE(pwd);
+
+ return(_status);
+ }
+
+int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char *ufrag, char *pwd, nr_ice_ctx **ctxp)
+ {
+ nr_ice_ctx *ctx=0;
+ int r,_status;
+
+ if(r=r_log_register("ice", &LOG_ICE))
+ ABORT(r);
+
+ if(!(ctx=RCALLOC(sizeof(nr_ice_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ ctx->flags=flags;
+
+ if(!(ctx->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+
+ if(!(ctx->ufrag=r_strdup(ufrag)))
+ ABORT(r);
+ if(!(ctx->pwd=r_strdup(pwd)))
+ ABORT(r);
+
+ /* Get the STUN servers */
+ if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX,
+ (unsigned int *)&ctx->stun_server_ct)||ctx->stun_server_ct==0) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No STUN servers specified in nICEr registry", ctx->label);
+ ctx->stun_server_ct=0;
+ }
+
+ /* 31 is the max for our priority algorithm */
+ if(ctx->stun_server_ct>31){
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN servers specified: max=31", ctx->label);
+ ctx->stun_server_ct=31;
+ }
+
+ if(ctx->stun_server_ct>0){
+ if(r=nr_ice_fetch_stun_servers(ctx->stun_server_ct,&ctx->stun_servers)){
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load STUN servers from registry", ctx->label);
+ ctx->stun_server_ct=0;
+ ABORT(r);
+ }
+ }
+
+#ifdef USE_TURN
+ /* Get the TURN servers */
+ if(r=NR_reg_get_child_count(NR_ICE_REG_TURN_SRV_PRFX,
+ (unsigned int *)&ctx->turn_server_ct)||ctx->turn_server_ct==0) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No TURN servers specified in nICEr registry", ctx->label);
+ ctx->turn_server_ct=0;
+ }
+#else
+ ctx->turn_server_ct=0;
+#endif /* USE_TURN */
+
+ ctx->local_addrs=0;
+ ctx->local_addr_ct=0;
+
+ /* 31 is the max for our priority algorithm */
+ if((ctx->stun_server_ct+ctx->turn_server_ct)>31){
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN/TURN servers specified: max=31", ctx->label);
+ ctx->turn_server_ct=31-ctx->stun_server_ct;
+ }
+
+#ifdef USE_TURN
+ if(ctx->turn_server_ct>0){
+ if(r=nr_ice_fetch_turn_servers(ctx->turn_server_ct,&ctx->turn_servers)){
+ ctx->turn_server_ct=0;
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load TURN servers from registry", ctx->label);
+ ABORT(r);
+ }
+ }
+#endif /* USE_TURN */
+
+
+ ctx->Ta = 20;
+
+ ctx->test_timer_divider = 0;
+
+ if (r=nr_socket_factory_create_int(NULL, &default_socket_factory_vtbl, &ctx->socket_factory))
+ ABORT(r);
+
+ if ((r=NR_reg_get_string((char *)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME, ctx->force_net_interface, sizeof(ctx->force_net_interface)))) {
+ if (r == R_NOT_FOUND) {
+ ctx->force_net_interface[0] = 0;
+ } else {
+ ABORT(r);
+ }
+ }
+
+ STAILQ_INIT(&ctx->streams);
+ STAILQ_INIT(&ctx->sockets);
+ STAILQ_INIT(&ctx->foundations);
+ STAILQ_INIT(&ctx->peers);
+ STAILQ_INIT(&ctx->ids);
+
+ *ctxp=ctx;
+
+ _status=0;
+ abort:
+ if(_status && ctx)
+ nr_ice_ctx_destroy_cb(0,0,ctx);
+
+ return(_status);
+ }
+
+static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_ctx *ctx=cb_arg;
+ nr_ice_foundation *f1,*f2;
+ nr_ice_media_stream *s1,*s2;
+ int i;
+ nr_ice_stun_id *id1,*id2;
+
+ STAILQ_FOREACH_SAFE(s1, &ctx->streams, entry, s2){
+ STAILQ_REMOVE(&ctx->streams,s1,nr_ice_media_stream_,entry);
+ nr_ice_media_stream_destroy(&s1);
+ }
+
+ RFREE(ctx->label);
+
+ RFREE(ctx->stun_servers);
+
+ RFREE(ctx->local_addrs);
+
+ for (i = 0; i < ctx->turn_server_ct; i++) {
+ RFREE(ctx->turn_servers[i].username);
+ r_data_destroy(&ctx->turn_servers[i].password);
+ }
+ RFREE(ctx->turn_servers);
+
+ f1=STAILQ_FIRST(&ctx->foundations);
+ while(f1){
+ f2=STAILQ_NEXT(f1,entry);
+ RFREE(f1);
+ f1=f2;
+ }
+ RFREE(ctx->pwd);
+ RFREE(ctx->ufrag);
+
+ STAILQ_FOREACH_SAFE(id1, &ctx->ids, entry, id2){
+ STAILQ_REMOVE(&ctx->ids,id1,nr_ice_stun_id_,entry);
+ RFREE(id1);
+ }
+
+ nr_resolver_destroy(&ctx->resolver);
+ nr_interface_prioritizer_destroy(&ctx->interface_prioritizer);
+ nr_socket_wrapper_factory_destroy(&ctx->turn_tcp_socket_wrapper);
+ nr_socket_factory_destroy(&ctx->socket_factory);
+
+ RFREE(ctx);
+ }
+
+void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags)
+ {
+ ctx->flags |= flags;
+ }
+
+void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags)
+ {
+ ctx->flags &= ~flags;
+ }
+
+int nr_ice_ctx_destroy(nr_ice_ctx **ctxp)
+ {
+ if(!ctxp || !*ctxp)
+ return(0);
+
+ (*ctxp)->done_cb=0;
+ (*ctxp)->trickle_cb=0;
+
+ NR_ASYNC_SCHEDULE(nr_ice_ctx_destroy_cb,*ctxp);
+
+ *ctxp=0;
+
+ return(0);
+ }
+
+void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg)
+ {
+ int r;
+ nr_ice_candidate *cand=cb_arg;
+ nr_ice_ctx *ctx;
+
+
+ assert(cb_arg);
+ if (!cb_arg)
+ return;
+ ctx = cand->ctx;
+
+ ctx->uninitialized_candidates--;
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): initialized, %d remaining",ctx->label,cand->codeword,ctx->uninitialized_candidates);
+
+ /* Avoid the need for yet another initialization function */
+ if (cand->state == NR_ICE_CAND_STATE_INITIALIZING && cand->type == HOST)
+ cand->state = NR_ICE_CAND_STATE_INITIALIZED;
+
+ if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) {
+ int was_pruned = 0;
+
+ if (r=nr_ice_component_maybe_prune_candidate(ctx, cand->component,
+ cand, &was_pruned)) {
+ r_log(LOG_ICE, LOG_NOTICE, "ICE(%s): Problem pruning candidates",ctx->label);
+ }
+
+ /* If we are initialized, the candidate wasn't pruned,
+ and we have a trickle ICE callback fire the callback */
+ if (ctx->trickle_cb && !was_pruned &&
+ !nr_ice_ctx_hide_candidate(ctx, cand)) {
+ ctx->trickle_cb(ctx->trickle_cb_arg, ctx, cand->stream, cand->component_id, cand);
+
+ if (nr_ice_ctx_pair_new_trickle_candidates(ctx, cand)) {
+ r_log(LOG_ICE,LOG_ERR, "ICE(%s): All could not pair new trickle candidate",ctx->label);
+ /* But continue */
+ }
+ }
+ }
+
+ if(ctx->uninitialized_candidates==0){
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label);
+ if (ctx->done_cb) {
+ ctx->done_cb(0,0,ctx->cb_arg);
+ }
+ else {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No done_cb. We were probably destroyed.",ctx->label);
+ }
+ }
+ else {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Waiting for %d candidates to be initialized",ctx->label, ctx->uninitialized_candidates);
+ }
+ }
+
+static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand)
+ {
+ int r,_status;
+ nr_ice_peer_ctx *pctx;
+
+ pctx=STAILQ_FIRST(&ctx->peers);
+ while(pctx){
+ if (pctx->state == NR_ICE_PEER_STATE_PAIRED) {
+ r = nr_ice_peer_ctx_pair_new_trickle_candidate(ctx, pctx, cand);
+ if (r)
+ ABORT(r);
+ }
+
+ pctx=STAILQ_NEXT(pctx,entry);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* Get the default address by doing a connect to a known public IP address,
+ in this case Google public DNS:
+
+ IPv4: 8.8.8.8
+ IPv6: 2001:4860:4860::8888
+
+ Then we can do getsockname to get the address. No packets get sent
+ since this is UDP. It's just a way to get the address.
+*/
+static int nr_ice_get_default_address(nr_ice_ctx *ctx, int ip_version, nr_transport_addr* addrp)
+ {
+ int r,_status;
+ nr_transport_addr addr;
+ nr_transport_addr remote_addr;
+ nr_socket *sock=0;
+
+ switch(ip_version) {
+ case NR_IPV4:
+ if ((r=nr_str_port_to_transport_addr("0.0.0.0", 0, IPPROTO_UDP, &addr)))
+ ABORT(r);
+ if ((r=nr_str_port_to_transport_addr("8.8.8.8", 53, IPPROTO_UDP, &remote_addr)))
+ ABORT(r);
+ break;
+ case NR_IPV6:
+ if ((r=nr_str_port_to_transport_addr("::0", 0, IPPROTO_UDP, &addr)))
+ ABORT(r);
+ if ((r=nr_str_port_to_transport_addr("2001:4860:4860::8888", 53, IPPROTO_UDP, &remote_addr)))
+ ABORT(r);
+ break;
+ default:
+ assert(0);
+ ABORT(R_INTERNAL);
+ }
+
+ if ((r=nr_socket_factory_create_socket(ctx->socket_factory, &addr, &sock)))
+ ABORT(r);
+ if ((r=nr_socket_connect(sock, &remote_addr)))
+ ABORT(r);
+ if ((r=nr_socket_getaddr(sock, addrp)))
+ ABORT(r);
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "Default address: %s", addrp->as_string);
+
+ _status=0;
+ abort:
+ nr_socket_destroy(&sock);
+ return(_status);
+ }
+
+static int nr_ice_get_default_local_address(nr_ice_ctx *ctx, int ip_version, nr_local_addr* addrs, int addr_ct, nr_local_addr *addrp)
+ {
+ int r,_status;
+ nr_transport_addr default_addr;
+ int i;
+
+ if ((r=nr_ice_get_default_address(ctx, ip_version, &default_addr)))
+ ABORT(r);
+
+ for (i=0; i < addr_ct; ++i) {
+ if (!nr_transport_addr_cmp(&default_addr, &addrs[i].addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) {
+ if ((r=nr_local_addr_copy(addrp, &addrs[i])))
+ ABORT(r);
+ break;
+ }
+ }
+
+ if (i == addr_ct) {
+ if ((r=nr_transport_addr_copy(&addrp->addr, &default_addr)))
+ ABORT(r);
+ strlcpy(addrp->addr.ifname, "default route", sizeof(addrp->addr.ifname));
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_get_local_addresses(nr_ice_ctx *ctx)
+ {
+ int r,_status;
+ nr_local_addr local_addrs[MAXADDRS];
+ nr_local_addr *addrs = 0;
+ int i,addr_ct;
+ nr_local_addr default_addrs[2];
+ int default_addr_ct = 0;
+
+ if (!ctx->local_addrs) {
+ /* First, gather all the local addresses we have */
+ if((r=nr_stun_find_local_addresses(local_addrs,MAXADDRS,&addr_ct))) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to gather local addresses, trying default route",ctx->label);
+ }
+
+ if (ctx->force_net_interface[0] && addr_ct) {
+ /* Limit us to only addresses on a single interface */
+ int force_addr_ct = 0;
+ for(i=0;i<addr_ct;i++){
+ if (!strcmp(local_addrs[i].addr.ifname, ctx->force_net_interface)) {
+ // copy it down in the array, if needed
+ if (i != force_addr_ct) {
+ if (r=nr_local_addr_copy(&local_addrs[force_addr_ct], &local_addrs[i])) {
+ ABORT(r);
+ }
+ }
+ force_addr_ct++;
+ }
+ }
+ addr_ct = force_addr_ct;
+ }
+
+ if ((!addr_ct) || (ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS)) {
+ /* Get just the default IPv4 and IPv6 addrs */
+ if(!nr_ice_get_default_local_address(ctx, NR_IPV4, local_addrs, addr_ct,
+ &default_addrs[default_addr_ct])) {
+ ++default_addr_ct;
+ }
+ if(!nr_ice_get_default_local_address(ctx, NR_IPV6, local_addrs, addr_ct,
+ &default_addrs[default_addr_ct])) {
+ ++default_addr_ct;
+ }
+ if (!default_addr_ct) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): failed to find default addresses",ctx->label);
+ ABORT(R_FAILED);
+ }
+ addrs = default_addrs;
+ addr_ct = default_addr_ct;
+ }
+ else {
+ addrs = local_addrs;
+ }
+
+ /* Sort interfaces by preference */
+ if(ctx->interface_prioritizer) {
+ for(i=0;i<addr_ct;i++){
+ if(r=nr_interface_prioritizer_add_interface(ctx->interface_prioritizer,addrs+i)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to add interface ",ctx->label);
+ ABORT(r);
+ }
+ }
+ if(r=nr_interface_prioritizer_sort_preference(ctx->interface_prioritizer)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to sort interface by preference",ctx->label);
+ ABORT(r);
+ }
+ }
+
+ if (r=nr_ice_ctx_set_local_addrs(ctx,addrs,addr_ct)) {
+ ABORT(r);
+ }
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg)
+ {
+ int r,_status;
+ nr_ice_media_stream *stream;
+
+ if ((r=nr_ice_get_local_addresses(ctx)))
+ ABORT(r);
+
+ if(STAILQ_EMPTY(&ctx->streams)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): Missing streams to initialize",ctx->label);
+ ABORT(R_BAD_ARGS);
+ }
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label);
+ ctx->done_cb=done_cb;
+ ctx->cb_arg=cb_arg;
+
+ /* Initialize all the media stream/component pairs */
+ stream=STAILQ_FIRST(&ctx->streams);
+ while(stream){
+ if(r=nr_ice_media_stream_initialize(ctx,stream))
+ ABORT(r);
+
+ stream=STAILQ_NEXT(stream,entry);
+ }
+
+ if(ctx->uninitialized_candidates)
+ ABORT(R_WOULDBLOCK);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp)
+ {
+ int r,_status;
+
+ if(r=nr_ice_media_stream_create(ctx,label,components,streamp))
+ ABORT(r);
+
+ STAILQ_INSERT_TAIL(&ctx->streams,*streamp,entry);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp)
+ {
+ int r,_status;
+ nr_ice_peer_ctx *pctx;
+ nr_ice_media_stream *peer_stream;
+
+ pctx=STAILQ_FIRST(&ctx->peers);
+ while(pctx){
+ if(!nr_ice_peer_ctx_find_pstream(pctx, *streamp, &peer_stream)) {
+ if(r=nr_ice_peer_ctx_remove_pstream(pctx, &peer_stream)) {
+ ABORT(r);
+ }
+ }
+
+ pctx=STAILQ_NEXT(pctx,entry);
+ }
+
+ STAILQ_REMOVE(&ctx->streams,*streamp,nr_ice_media_stream_,entry);
+ if(r=nr_ice_media_stream_destroy(streamp)) {
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp)
+ {
+ char **attrs=0;
+ int _status;
+ char *tmp=0;
+
+ if(!(attrs=RCALLOC(sizeof(char *)*2)))
+ ABORT(R_NO_MEMORY);
+
+ if(!(tmp=RMALLOC(100)))
+ ABORT(R_NO_MEMORY);
+ snprintf(tmp,100,"ice-ufrag:%s",ctx->ufrag);
+ attrs[0]=tmp;
+
+ if(!(tmp=RMALLOC(100)))
+ ABORT(R_NO_MEMORY);
+ snprintf(tmp,100,"ice-pwd:%s",ctx->pwd);
+ attrs[1]=tmp;
+
+ *attrctp=2;
+ *attrsp=attrs;
+
+ _status=0;
+ abort:
+ if (_status){
+ if (attrs){
+ RFREE(attrs[0]);
+ RFREE(attrs[1]);
+ }
+ RFREE(attrs);
+ }
+ return(_status);
+ }
+
+static int nr_ice_random_string(char *str, int len)
+ {
+ unsigned char bytes[100];
+ int needed;
+ int r,_status;
+
+ if(len%2) ABORT(R_BAD_ARGS);
+ needed=len/2;
+
+ if(needed>sizeof(bytes)) ABORT(R_BAD_ARGS);
+
+ if(r=nr_crypto_random_bytes(bytes,needed))
+ ABORT(r);
+
+ if(r=nr_bin2hex(bytes,needed,(unsigned char *)str))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* This is incredibly annoying: we now have a datagram but we don't
+ know which peer it's from, and we need to be able to tell the
+ API user. So, offer it to each peer and if one bites, assume
+ the others don't want it
+*/
+int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len)
+ {
+ nr_ice_peer_ctx *pctx;
+ int r;
+
+ pctx=STAILQ_FIRST(&ctx->peers);
+ while(pctx){
+ r=nr_ice_peer_ctx_deliver_packet_maybe(pctx, comp, source_addr, data, len);
+ if(!r)
+ break;
+
+ pctx=STAILQ_NEXT(pctx,entry);
+ }
+
+ if(!pctx)
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Packet received from %s which doesn't match any known peer",ctx->label,source_addr->as_string);
+
+ return(0);
+ }
+
+int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12])
+ {
+ nr_ice_stun_id *xid;
+
+ xid=STAILQ_FIRST(&ctx->ids);
+ while(xid){
+ if (!memcmp(xid->id, id, 12))
+ return 1;
+
+ xid=STAILQ_NEXT(xid,entry);
+ }
+
+ return 0;
+ }
+
+int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg)
+{
+ int _status;
+ nr_ice_stun_id *xid;
+
+ xid = RCALLOC(sizeof(*xid));
+ if (!xid)
+ ABORT(R_NO_MEMORY);
+
+ assert(sizeof(xid->id) == sizeof(msg->header.id));
+#if __STDC_VERSION__ >= 201112L
+ _Static_assert(sizeof(xid->id) == sizeof(msg->header.id),"Message ID Size Mismatch");
+#endif
+ memcpy(xid->id, &msg->header.id, sizeof(xid->id));
+
+ STAILQ_INSERT_TAIL(&ctx->ids,xid,entry);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+
+/* Clean up some of the resources (mostly file descriptors) used
+ by candidates we didn't choose. Note that this still leaves
+ a fair amount of non-system stuff floating around. This gets
+ cleaned up when you destroy the ICE ctx */
+int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *lstr,*rstr;
+
+ r_log(LOG_ICE,LOG_DEBUG,"Finalizing ICE ctx %s, peer=%s",ctx->label,pctx->label);
+ /*
+ First find the peer stream, if any
+ */
+ lstr=STAILQ_FIRST(&ctx->streams);
+ while(lstr){
+ rstr=STAILQ_FIRST(&pctx->peer_streams);
+
+ while(rstr){
+ if(rstr->local_stream==lstr)
+ break;
+
+ rstr=STAILQ_NEXT(rstr,entry);
+ }
+
+ nr_ice_media_stream_finalize(lstr,rstr);
+
+ lstr=STAILQ_NEXT(lstr,entry);
+ }
+
+ return(0);
+ }
+
+
+int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg)
+{
+ ctx->trickle_cb = cb;
+ ctx->trickle_cb_arg = cb_arg;
+
+ return 0;
+}
+
+int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand)
+ {
+ if (cand->state != NR_ICE_CAND_STATE_INITIALIZED) {
+ return 1;
+ }
+
+ if (ctx->flags & NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES) {
+ if (cand->type == HOST)
+ return 1;
+ }
+
+ return 0;
+ }
+
+int nr_ice_get_new_ice_ufrag(char** ufrag)
+ {
+ int r,_status;
+ char buf[ICE_UFRAG_LEN+1];
+
+ if(r=nr_ice_random_string(buf,ICE_UFRAG_LEN))
+ ABORT(r);
+ if(!(*ufrag=r_strdup(buf)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if(_status) {
+ RFREE(*ufrag);
+ *ufrag = 0;
+ }
+ return(_status);
+ }
+
+int nr_ice_get_new_ice_pwd(char** pwd)
+ {
+ int r,_status;
+ char buf[ICE_PWD_LEN+1];
+
+ if(r=nr_ice_random_string(buf,ICE_PWD_LEN))
+ ABORT(r);
+ if(!(*pwd=r_strdup(buf)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if(_status) {
+ RFREE(*pwd);
+ *pwd = 0;
+ }
+ return(_status);
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
new file mode 100644
index 000000000..cc79304f0
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -0,0 +1,203 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_ctx_h
+#define _ice_ctx_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+/* Not good practice but making includes simpler */
+#include "transport_addr.h"
+#include "nr_socket.h"
+#include "nr_resolver.h"
+#include "nr_interface_prioritizer.h"
+#include "nr_socket_wrapper.h"
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "turn_client_ctx.h"
+
+#define NR_ICE_STUN_SERVER_TYPE_ADDR 1
+#define NR_ICE_STUN_SERVER_TYPE_DNSNAME 2
+
+typedef struct nr_ice_stun_server_ {
+ int type;
+ union {
+ nr_transport_addr addr;
+ struct {
+ char host[256]; /* Limit from RFC 1034, plus a 0 byte */
+ UINT2 port;
+ } dnsname;
+ } u;
+ int id;
+ int transport;
+} nr_ice_stun_server;
+
+typedef struct nr_ice_turn_server_ {
+ nr_ice_stun_server turn_server;
+ char *username;
+ Data *password;
+} nr_ice_turn_server;
+
+typedef struct nr_ice_foundation_ {
+ int index;
+
+ nr_transport_addr addr;
+ int type;
+ nr_ice_stun_server *stun_server;
+
+ STAILQ_ENTRY(nr_ice_foundation_) entry;
+} nr_ice_foundation;
+
+typedef STAILQ_HEAD(nr_ice_foundation_head_,nr_ice_foundation_) nr_ice_foundation_head;
+
+typedef TAILQ_HEAD(nr_ice_candidate_head_,nr_ice_candidate_) nr_ice_candidate_head;
+typedef TAILQ_HEAD(nr_ice_cand_pair_head_,nr_ice_cand_pair_) nr_ice_cand_pair_head;
+typedef struct nr_ice_component_ nr_ice_component;
+typedef struct nr_ice_media_stream_ nr_ice_media_stream;
+typedef struct nr_ice_ctx_ nr_ice_ctx;
+typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx;
+typedef struct nr_ice_candidate_ nr_ice_candidate;
+typedef struct nr_ice_cand_pair_ nr_ice_cand_pair;
+typedef void (*nr_ice_trickle_candidate_cb) (void *cb_arg,
+ nr_ice_ctx *ctx, nr_ice_media_stream *stream, int component_id,
+ nr_ice_candidate *candidate);
+
+#include "ice_socket.h"
+#include "ice_component.h"
+#include "ice_media_stream.h"
+#include "ice_candidate.h"
+#include "ice_candidate_pair.h"
+#include "ice_handler.h"
+#include "ice_peer_ctx.h"
+
+typedef struct nr_ice_stun_id_ {
+ UCHAR id[12];
+
+ STAILQ_ENTRY(nr_ice_stun_id_) entry;
+} nr_ice_stun_id;
+
+typedef STAILQ_HEAD(nr_ice_stun_id_head_,nr_ice_stun_id_) nr_ice_stun_id_head;
+
+struct nr_ice_ctx_ {
+ UINT4 flags;
+ char *label;
+
+ char *ufrag;
+ char *pwd;
+
+ UINT4 Ta;
+
+ nr_ice_stun_server *stun_servers; /* The list of stun servers */
+ int stun_server_ct;
+ nr_ice_turn_server *turn_servers; /* The list of turn servers */
+ int turn_server_ct;
+ nr_local_addr *local_addrs; /* The list of available local addresses and corresponding interface information */
+ int local_addr_ct;
+
+ nr_resolver *resolver; /* The resolver to use */
+ nr_interface_prioritizer *interface_prioritizer; /* Priority decision logic */
+ nr_socket_wrapper_factory *turn_tcp_socket_wrapper; /* The TURN TCP socket wrapper to use */
+ nr_socket_factory *socket_factory;
+
+ nr_ice_foundation_head foundations;
+
+ nr_ice_media_stream_head streams; /* Media streams */
+ int stream_ct;
+ nr_ice_socket_head sockets; /* The sockets we're using */
+ int uninitialized_candidates;
+
+ UINT4 gather_rto;
+ UINT4 stun_delay;
+
+ UINT4 test_timer_divider;
+
+ nr_ice_peer_ctx_head peers;
+ nr_ice_stun_id_head ids;
+
+ NR_async_cb done_cb;
+ void *cb_arg;
+
+ nr_ice_trickle_candidate_cb trickle_cb;
+ void *trickle_cb_arg;
+
+ char force_net_interface[MAXIFNAME];
+};
+
+int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp);
+int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char* ufrag, char* pwd, nr_ice_ctx **ctxp);
+#define NR_ICE_CTX_FLAGS_OFFERER 1
+#define NR_ICE_CTX_FLAGS_ANSWERER (1<<1)
+#define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1<<2)
+#define NR_ICE_CTX_FLAGS_LITE (1<<3)
+#define NR_ICE_CTX_FLAGS_RELAY_ONLY (1<<4)
+#define NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES (1<<5)
+#define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS (1<<6)
+#define NR_ICE_CTX_FLAGS_ONLY_PROXY (1<<7)
+
+void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags);
+void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags);
+int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
+int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
+int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
+void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg);
+int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
+int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp);
+int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp);
+int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
+int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]);
+int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg);
+int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx);
+int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers, int ct);
+int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int ct);
+int nr_ice_ctx_copy_turn_servers(nr_ice_ctx *ctx, nr_ice_turn_server *servers, int ct);
+int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver);
+int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *prioritizer);
+int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper);
+void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory);
+int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg);
+int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
+int nr_ice_get_new_ice_ufrag(char** ufrag);
+int nr_ice_get_new_ice_pwd(char** pwd);
+
+#define NR_ICE_MAX_ATTRIBUTE_SIZE 256
+
+extern int LOG_ICE;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_handler.h b/media/mtransport/third_party/nICEr/src/ice/ice_handler.h
new file mode 100644
index 000000000..5a0690ada
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_handler.h
@@ -0,0 +1,84 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_h
+#define _ice_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct nr_ice_handler_vtbl_ {
+ /* The checks on this media stream are done. The handler needs to
+ select a single pair to proceed with (regular nomination).
+ Once this returns the check starts and the pair can be
+ written on. Use nr_ice_candidate_pair_select() to perform the
+ selection.
+ TODO: !ekr! is this right?
+ */
+ int (*select_pair)(void *obj,nr_ice_media_stream *stream,
+int component_id, nr_ice_cand_pair **potentials,int potential_ct);
+
+ /* This media stream is ready to read/write (aggressive nomination).
+ May be called again if the nominated pair changes due to
+ ICE instability. TODO: !ekr! think about this
+ */
+ int (*stream_ready)(void *obj, nr_ice_media_stream *stream);
+
+ /* This media stream has failed */
+ int (*stream_failed)(void *obj, nr_ice_media_stream *stream);
+
+ /* ICE is connected for this peer ctx */
+ int (*ice_connected)(void *obj, nr_ice_peer_ctx *pctx);
+
+ /* A message was delivered to us */
+ int (*msg_recvd)(void *obj, nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int component_id, UCHAR *msg, int len);
+
+ /* ICE has started checking. */
+ int (*ice_checking)(void *obj, nr_ice_peer_ctx *pctx);
+
+ /* ICE detected a (temporary?) disconnect. */
+ int (*ice_disconnected)(void *obj, nr_ice_peer_ctx *pctx);
+} nr_ice_handler_vtbl;
+
+typedef struct nr_ice_handler_ {
+ void *obj;
+ nr_ice_handler_vtbl *vtbl;
+} nr_ice_handler;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
new file mode 100644
index 000000000..b93e45aeb
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
@@ -0,0 +1,933 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_media_stream.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <string.h>
+#include <assert.h>
+#include <nr_api.h>
+#include <r_assoc.h>
+#include <async_timer.h>
+#include "ice_util.h"
+#include "ice_ctx.h"
+
+static char *nr_ice_media_stream_states[]={"INVALID",
+ "UNPAIRED","FROZEN","ACTIVE","COMPLETED","FAILED"
+};
+
+int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
+
+int nr_ice_media_stream_create(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp)
+ {
+ int r,_status;
+ nr_ice_media_stream *stream=0;
+ nr_ice_component *comp=0;
+ int i;
+
+ if(!(stream=RCALLOC(sizeof(nr_ice_media_stream))))
+ ABORT(R_NO_MEMORY);
+
+ if(!(stream->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+
+ stream->ctx=ctx;
+
+ STAILQ_INIT(&stream->components);
+ for(i=0;i<components;i++){
+ /* component-id must be > 0, so increment by 1 */
+ if(r=nr_ice_component_create(stream, i+1, &comp))
+ ABORT(r);
+
+ }
+
+ TAILQ_INIT(&stream->check_list);
+ TAILQ_INIT(&stream->trigger_check_queue);
+
+ stream->component_ct=components;
+ stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED;
+ *streamp=stream;
+
+ _status=0;
+ abort:
+ if(_status){
+ nr_ice_media_stream_destroy(&stream);
+ }
+ return(_status);
+ }
+
+int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp)
+ {
+ nr_ice_media_stream *stream;
+ nr_ice_component *c1,*c2;
+ nr_ice_cand_pair *p1,*p2;
+ if(!streamp || !*streamp)
+ return(0);
+
+ stream=*streamp;
+ *streamp=0;
+
+ STAILQ_FOREACH_SAFE(c1, &stream->components, entry, c2){
+ STAILQ_REMOVE(&stream->components,c1,nr_ice_component_,entry);
+ nr_ice_component_destroy(&c1);
+ }
+
+ /* Note: all the entries from the trigger check queue are held in here as
+ * well, so we only clean up the super set. */
+ TAILQ_FOREACH_SAFE(p1, &stream->check_list, check_queue_entry, p2){
+ TAILQ_REMOVE(&stream->check_list,p1,check_queue_entry);
+ nr_ice_candidate_pair_destroy(&p1);
+ }
+
+ RFREE(stream->label);
+
+ RFREE(stream->ufrag);
+ RFREE(stream->pwd);
+ RFREE(stream->r2l_user);
+ RFREE(stream->l2r_user);
+ r_data_zfree(&stream->r2l_pass);
+ r_data_zfree(&stream->l2r_pass);
+
+ if(stream->timer)
+ NR_async_timer_cancel(stream->timer);
+
+ RFREE(stream);
+
+ return(0);
+ }
+
+int nr_ice_media_stream_initialize(nr_ice_ctx *ctx, nr_ice_media_stream *stream)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if(r=nr_ice_component_initialize(ctx,comp))
+ ABORT(r);
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp, int *attrctp)
+ {
+ int attrct=0;
+ nr_ice_component *comp;
+ char **attrs=0;
+ int index=0;
+ nr_ice_candidate *cand;
+ int r,_status;
+
+ *attrctp=0;
+
+ /* First find out how many attributes we need */
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if (comp->state != NR_ICE_COMPONENT_DISABLED) {
+ cand = TAILQ_FIRST(&comp->candidates);
+ while(cand){
+ if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) {
+ ++attrct;
+ }
+
+ cand = TAILQ_NEXT(cand, entry_comp);
+ }
+ }
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ if(attrct < 1){
+ r_log(LOG_ICE,LOG_ERR,"ICE-STREAM(%s): Failed to find any components for stream",stream->label);
+ ABORT(R_FAILED);
+ }
+
+ /* Make the array we'll need */
+ if(!(attrs=RCALLOC(sizeof(char *)*attrct)))
+ ABORT(R_NO_MEMORY);
+ for(index=0;index<attrct;index++){
+ if(!(attrs[index]=RMALLOC(NR_ICE_MAX_ATTRIBUTE_SIZE)))
+ ABORT(R_NO_MEMORY);
+ }
+
+ index=0;
+ /* Now format the attributes */
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if (comp->state != NR_ICE_COMPONENT_DISABLED) {
+ nr_ice_candidate *cand;
+
+ cand=TAILQ_FIRST(&comp->candidates);
+ while(cand){
+ if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) {
+ assert(index < attrct);
+
+ if (index >= attrct)
+ ABORT(R_INTERNAL);
+
+ if(r=nr_ice_format_candidate_attribute(cand, attrs[index],NR_ICE_MAX_ATTRIBUTE_SIZE))
+ ABORT(r);
+
+ index++;
+ }
+
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+ }
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ *attrsp=attrs;
+ *attrctp=attrct;
+
+ _status=0;
+ abort:
+ if(_status){
+ if(attrs){
+ for(index=0;index<attrct;index++){
+ RFREE(attrs[index]);
+ }
+ RFREE(attrs);
+ }
+ }
+ return(_status);
+ }
+
+/* Get a default candidate per 4.1.4 */
+int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if (comp->component_id == component)
+ break;
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ if (!comp)
+ ABORT(R_NOT_FOUND);
+
+ /* If there aren't any IPV4 candidates, try IPV6 */
+ if((r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV4)) &&
+ (r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV6))) {
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream)
+ {
+ int r,_status;
+ nr_ice_component *pcomp,*lcomp;
+
+ pcomp=STAILQ_FIRST(&pstream->components);
+ lcomp=STAILQ_FIRST(&lstream->components);
+ while(pcomp){
+ if ((lcomp->state != NR_ICE_COMPONENT_DISABLED) &&
+ (pcomp->state != NR_ICE_COMPONENT_DISABLED)) {
+ if(r=nr_ice_component_pair_candidates(pctx,lcomp,pcomp))
+ ABORT(r);
+ }
+
+ lcomp=STAILQ_NEXT(lcomp,entry);
+ pcomp=STAILQ_NEXT(pcomp,entry);
+ };
+
+ if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) {
+ nr_ice_media_stream_set_state(pstream, NR_ICE_MEDIA_STREAM_CHECKS_FROZEN);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, nr_ice_media_stream *pstream, int *serviced)
+ {
+ nr_ice_component *pcomp;
+ int r,_status;
+ char *user = 0;
+
+ if (serviced)
+ *serviced = 0;
+
+ pcomp=STAILQ_FIRST(&pstream->components);
+ while(pcomp){
+ int serviced_inner=0;
+
+ /* Flush all the pre-answer requests */
+ if(r=nr_ice_component_service_pre_answer_requests(pctx, pcomp, pstream->r2l_user, &serviced_inner))
+ ABORT(r);
+ if (serviced)
+ *serviced += serviced_inner;
+
+ pcomp=STAILQ_NEXT(pcomp,entry);
+ }
+
+ _status=0;
+ abort:
+ RFREE(user);
+ return(_status);
+ }
+
+/* S 5.8 -- run the first pair from the triggered check queue (even after
+ * checks have completed S 8.1.2) or run the highest priority WAITING pair or
+ * if not available FROZEN pair from the check queue */
+static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg)
+ {
+ int r,_status;
+ nr_ice_media_stream *stream=cb_arg;
+ nr_ice_cand_pair *pair = 0;
+ int timer_multiplier=stream->pctx->active_streams ? stream->pctx->active_streams : 1;
+ int timer_val=stream->pctx->ctx->Ta*timer_multiplier;
+
+ assert(timer_val>0);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label);
+ stream->timer=0;
+
+ /* The trigger check queue has the highest priority */
+ pair=TAILQ_FIRST(&stream->trigger_check_queue);
+ while(pair){
+ if(pair->state==NR_ICE_PAIR_STATE_WAITING){
+ /* Remove the pair from he trigger check queue */
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Removing pair from trigger check queue %s",stream->pctx->label,pair->as_string);
+ TAILQ_REMOVE(&stream->trigger_check_queue,pair,triggered_check_queue_entry);
+ break;
+ }
+ pair=TAILQ_NEXT(pair,triggered_check_queue_entry);
+ }
+
+ if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
+ if(!pair){
+ /* Find the highest priority WAITING check and move it to RUNNING */
+ pair=TAILQ_FIRST(&stream->check_list);
+ while(pair){
+ if(pair->state==NR_ICE_PAIR_STATE_WAITING)
+ break;
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+ }
+
+ /* Hmmm... No WAITING. Let's look for FROZEN */
+ if(!pair){
+ pair=TAILQ_FIRST(&stream->check_list);
+
+ while(pair){
+ if(pair->state==NR_ICE_PAIR_STATE_FROZEN){
+ if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair))
+ ABORT(r);
+ break;
+ }
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+ }
+ }
+
+ if(pair){
+ nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */
+ NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer);
+ }
+ else {
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): no pairs for %s",stream->pctx->label,stream->label);
+ }
+
+ _status=0;
+ abort:
+ return;
+ }
+
+/* Start checks for this media stream (aka check list) */
+int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
+ {
+ int r,_status;
+
+ /* Don't start the check timer if the stream is failed */
+ if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) {
+ assert(0);
+ ABORT(R_INTERNAL);
+ }
+
+ /* Even if the stream is completed already remote can still create a new
+ * triggered check request which needs to fire, but not change our stream
+ * state. */
+ if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
+ if(r=nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)) {
+ ABORT(r);
+ }
+ }
+
+ if (!stream->timer) {
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): Starting check timer for stream.",pctx->label,stream->label);
+ nr_ice_media_stream_check_timer_cb(0,0,stream);
+ }
+
+ nr_ice_peer_ctx_stream_started_checks(pctx, stream);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* Start checks for this media stream (aka check list) S 5.7 */
+int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
+ {
+ int r,_status;
+ r_assoc *assoc=0;
+ nr_ice_cand_pair *pair=0;
+
+ /* Already seen assoc */
+ if(r=r_assoc_create(&assoc,r_assoc_crc32_hash_compute,5))
+ ABORT(r);
+
+ /* S 5.7.4. Set the highest priority pairs in each foundation to WAITING */
+ pair=TAILQ_FIRST(&stream->check_list);
+ while(pair){
+ void *v;
+
+ if(r=r_assoc_fetch(assoc,pair->foundation,strlen(pair->foundation),&v)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ if(r=nr_ice_candidate_pair_unfreeze(pctx,pair))
+ ABORT(r);
+
+ if(r=r_assoc_insert(assoc,pair->foundation,strlen(pair->foundation),
+ 0,0,0,R_ASSOC_NEW))
+ ABORT(r);
+ }
+
+ /* Already exists... fall through */
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+
+ _status=0;
+ abort:
+ r_assoc_destroy(&assoc);
+ return(_status);
+ }
+
+static int nr_ice_media_stream_unfreeze_pairs_match(nr_ice_media_stream *stream, char *foundation)
+ {
+ nr_ice_cand_pair *pair;
+ int r,_status;
+ int unfroze=0;
+
+ pair=TAILQ_FIRST(&stream->check_list);
+ while(pair){
+ if(pair->state==NR_ICE_PAIR_STATE_FROZEN &&
+ !strcmp(foundation,pair->foundation)){
+ if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair))
+ ABORT(r);
+ unfroze++;
+ }
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+
+ if(!unfroze)
+ return(R_NOT_FOUND);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* S 7.1.2.2 */
+int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation)
+ {
+ int r,_status;
+ nr_ice_media_stream *str;
+ nr_ice_component *comp;
+ int invalid_comps=0;
+
+ /* 1. Unfreeze all frozen pairs with the same foundation
+ in this stream */
+ if(r=nr_ice_media_stream_unfreeze_pairs_match(stream,foundation)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+
+ /* 2. See if there is a pair in the valid list for every component */
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if(!comp->valid_pairs)
+ invalid_comps++;
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ /* If there is a pair in the valid list for every component... */
+ /* Now go through the check lists for the other streams */
+ str=STAILQ_FIRST(&stream->pctx->peer_streams);
+ while(str){
+ if(str!=stream){
+ switch(str->ice_state){
+ case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE:
+ /* Unfreeze matching pairs */
+ if(r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation)){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ }
+ break;
+ case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN:
+ /* Unfreeze matching pairs if any */
+ r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation);
+ if(r){
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+
+ /* OK, no matching pairs: execute the algorithm from 5.7
+ for this stream */
+ if(r=nr_ice_media_stream_unfreeze_pairs(str->pctx,str))
+ ABORT(r);
+ }
+ if(r=nr_ice_media_stream_start_checks(str->pctx,str))
+ ABORT(r);
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ str=STAILQ_NEXT(str,entry);
+ }
+
+/* nr_ice_media_stream_dump_state(stream->pctx,stream,stderr); */
+
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out)
+ {
+ nr_ice_cand_pair *pair;
+
+ /* r_log(LOG_ICE,LOG_DEBUG,"MEDIA-STREAM(%s): state dump", stream->label); */
+ pair=TAILQ_FIRST(&stream->check_list);
+ while(pair){
+ nr_ice_candidate_pair_dump_state(pair,out);
+
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+
+ return(0);
+ }
+
+int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state)
+ {
+ /* Make no-change a no-op */
+ if (state == str->ice_state)
+ return 0;
+
+ assert(state < sizeof(nr_ice_media_stream_states)/sizeof(char *));
+ assert(str->ice_state < sizeof(nr_ice_media_stream_states)/sizeof(char *));
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): stream %s state %s->%s",
+ str->pctx->label,str->label,
+ nr_ice_media_stream_states[str->ice_state],
+ nr_ice_media_stream_states[state]);
+
+ if(state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)
+ str->pctx->active_streams++;
+ if(str->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)
+ str->pctx->active_streams--;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): %d active streams",
+ str->pctx->label, str->pctx->active_streams);
+
+ str->ice_state=state;
+
+ return(0);
+ }
+
+/* S OK, this component has a nominated. If every component has a nominated,
+ the stream is ready */
+int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if((comp->state != NR_ICE_COMPONENT_DISABLED) &&
+ (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) &&
+ !comp->nominated)
+ break;
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ /* At least one un-nominated component */
+ if(comp)
+ goto done;
+
+ /* All done... */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all active components have nominated candidate pairs",stream->pctx->label,stream->label);
+ nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED);
+
+ /* Cancel our timer */
+ if(stream->timer){
+ NR_async_timer_cancel(stream->timer);
+ stream->timer=0;
+ }
+
+ if (stream->pctx->handler) {
+ stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream);
+ }
+
+ /* Now tell the peer_ctx that we're connected */
+ if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx))
+ ABORT(r);
+
+ done:
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component)
+ {
+ int r,_status;
+ nr_ice_cand_pair *p2;
+
+ component->state=NR_ICE_COMPONENT_FAILED;
+
+ /* at least one component failed in this media stream, so the entire
+ * media stream is marked failed */
+
+ nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_FAILED);
+
+ /* OK, we need to cancel off everything on this component */
+ p2=TAILQ_FIRST(&stream->check_list);
+ while(p2){
+ if(r=nr_ice_candidate_pair_cancel(p2->pctx,p2,0))
+ ABORT(r);
+
+ p2=TAILQ_NEXT(p2,check_queue_entry);
+ }
+
+ /* Cancel our timer */
+ if(stream->timer){
+ NR_async_timer_cancel(stream->timer);
+ stream->timer=0;
+ }
+
+ if (stream->pctx->handler) {
+ stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream);
+ }
+
+ /* Now tell the peer_ctx that we're connected */
+ if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp)
+ {
+ nr_ice_candidate *cand;
+ nr_ice_candidate *best_cand=0;
+ nr_ice_component *comp;
+ int r,_status;
+
+ if(r=nr_ice_media_stream_find_component(str,component,&comp))
+ ABORT(r);
+
+ cand=TAILQ_FIRST(&comp->candidates);
+ while(cand){
+ if(cand->state==NR_ICE_CAND_STATE_INITIALIZED){
+ if(!best_cand || (cand->priority>best_cand->priority))
+ best_cand=cand;
+
+ }
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+
+ if(!best_cand)
+ ABORT(R_NOT_FOUND);
+
+ *candp=best_cand;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+/* OK, we have the stream the user created, but that reflects the base
+ ICE ctx, not the peer_ctx. So, find the related stream in the pctx,
+ and then find the component */
+int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp)
+ {
+ int _status;
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&str->components);
+ while(comp){
+ if(comp->component_id==comp_id)
+ break;
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ if(!comp)
+ ABORT(R_NOT_FOUND);
+
+ *compp=comp;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ /* First find the peer component */
+ if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp))
+ ABORT(r);
+
+ /* Do we have an active pair yet? We should... */
+ if(!comp->active)
+ ABORT(R_NOT_FOUND);
+
+ /* Does fresh ICE consent exist? */
+ if(!comp->can_send)
+ ABORT(R_FAILED);
+
+ /* OK, write to that pair, which means:
+ 1. Use the socket on our local side.
+ 2. Use the address on the remote side
+ */
+ if(r=nr_socket_sendto(comp->active->local->osock,data,len,0,
+ &comp->active->remote->addr))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* Returns R_REJECTED if the component is unpaired or has been disabled. */
+int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ /* First find the peer component */
+ if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp))
+ ABORT(r);
+
+ if (comp->state == NR_ICE_COMPONENT_UNPAIRED ||
+ comp->state == NR_ICE_COMPONENT_DISABLED)
+ ABORT(R_REJECTED);
+
+ if(!comp->active)
+ ABORT(R_NOT_FOUND);
+
+ if (local) *local = comp->active->local;
+ if (remote) *remote = comp->active->remote;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ /* First find the peer component */
+ if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp))
+ ABORT(r);
+
+ /* Do we have an active pair yet? We should... */
+ if(!comp->active)
+ ABORT(R_BAD_ARGS);
+
+ /* Use the socket on our local side */
+ if(r=nr_socket_getaddr(comp->active->local->osock,local))
+ ABORT(r);
+
+ /* Use the address on the remote side */
+ if(r=nr_transport_addr_copy(remote,&comp->active->remote->addr))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+
+int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr)
+ {
+ nr_ice_component *lcomp,*rcomp;
+
+ r_log(LOG_ICE,LOG_DEBUG,"Finalizing media stream %s, peer=%s",lstr->label,
+ rstr?rstr->label:"NONE");
+
+ lcomp=STAILQ_FIRST(&lstr->components);
+ if(rstr)
+ rcomp=STAILQ_FIRST(&rstr->components);
+ else
+ rcomp=0;
+
+ while(lcomp){
+ nr_ice_component_finalize(lcomp,rcomp);
+
+ lcomp=STAILQ_NEXT(lcomp,entry);
+ if(rcomp){
+ rcomp=STAILQ_NEXT(rcomp,entry);
+ }
+ }
+
+ return(0);
+ }
+
+int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ if ((r=nr_ice_media_stream_find_component(pstream, cand->component_id, &comp)))
+ ABORT(R_NOT_FOUND);
+
+ if (r=nr_ice_component_pair_candidate(pctx, comp, cand, 1))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int
+component_id, int *can_send, struct timeval *ts)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp)))
+ ABORT(r);
+
+ *can_send = comp->can_send;
+ ts->tv_sec = comp->consent_last_seen.tv_sec;
+ ts->tv_usec = comp->consent_last_seen.tv_usec;
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id)
+ {
+ int r,_status;
+ nr_ice_component *comp;
+
+ if (stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED)
+ ABORT(R_FAILED);
+
+ if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp)))
+ ABORT(r);
+
+ /* Can only disable before pairing */
+ if (comp->state != NR_ICE_COMPONENT_UNPAIRED &&
+ comp->state != NR_ICE_COMPONENT_DISABLED)
+ ABORT(R_FAILED);
+
+ comp->state = NR_ICE_COMPONENT_DISABLED;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+void nr_ice_media_stream_role_change(nr_ice_media_stream *stream)
+ {
+ nr_ice_cand_pair *pair,*temp_pair;
+ /* Changing role causes candidate pair priority to change, which requires
+ * re-sorting the check list. */
+ nr_ice_cand_pair_head old_checklist;
+
+ assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED);
+
+ /* Move check_list to old_checklist (not POD, have to do the hard way) */
+ TAILQ_INIT(&old_checklist);
+ TAILQ_FOREACH_SAFE(pair,&stream->check_list,check_queue_entry,temp_pair) {
+ TAILQ_REMOVE(&stream->check_list,pair,check_queue_entry);
+ TAILQ_INSERT_TAIL(&old_checklist,pair,check_queue_entry);
+ }
+
+ /* Re-insert into the check list */
+ TAILQ_FOREACH_SAFE(pair,&old_checklist,check_queue_entry,temp_pair) {
+ TAILQ_REMOVE(&old_checklist,pair,check_queue_entry);
+ nr_ice_candidate_pair_role_change(pair);
+ nr_ice_candidate_pair_insert(&stream->check_list,pair);
+ }
+ }
+
+int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *lcand, nr_ice_candidate *rcand, nr_ice_cand_pair **pair)
+ {
+ nr_ice_cand_pair_head *head = &str->check_list;
+ nr_ice_cand_pair *c1;
+
+ c1=TAILQ_FIRST(head);
+ while(c1){
+ if(c1->local == lcand &&
+ c1->remote == rcand) {
+ *pair=c1;
+ return(0);
+ }
+
+ c1=TAILQ_NEXT(c1,check_queue_entry);
+ }
+
+ return(R_NOT_FOUND);
+ }
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
new file mode 100644
index 000000000..3d818d530
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
@@ -0,0 +1,108 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_media_stream_h
+#define _ice_media_stream_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+struct nr_ice_media_stream_ {
+ char *label;
+ struct nr_ice_ctx_ *ctx;
+ struct nr_ice_peer_ctx_ *pctx;
+
+ struct nr_ice_media_stream_ *local_stream; /* used when this is a peer */
+ int component_ct;
+ nr_ice_component_head components;
+
+ char *ufrag; /* ICE username */
+ char *pwd; /* ICE password */
+ char *r2l_user; /* The username for incoming requests */
+ char *l2r_user; /* The username for outgoing requests */
+ Data r2l_pass; /* The password for incoming requests */
+ Data l2r_pass; /* The password for outcoming requests */
+ int ice_state;
+
+#define NR_ICE_MEDIA_STREAM_UNPAIRED 1
+#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2
+#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 3
+#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 4
+#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5
+
+ nr_ice_cand_pair_head check_list;
+ nr_ice_cand_pair_head trigger_check_queue;
+ void *timer; /* Check list periodic timer */
+
+/* nr_ice_cand_pair_head valid_list; */
+
+ STAILQ_ENTRY(nr_ice_media_stream_) entry;
+};
+
+typedef STAILQ_HEAD(nr_ice_media_stream_head_,nr_ice_media_stream_) nr_ice_media_stream_head;
+
+int nr_ice_media_stream_create(struct nr_ice_ctx_ *ctx,char *label, int components, nr_ice_media_stream **streamp);
+int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp);
+int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr);
+int nr_ice_media_stream_initialize(struct nr_ice_ctx_ *ctx, nr_ice_media_stream *stream);
+int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp,int *attrctp);
+int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp);
+int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream);
+int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream, int *serviced);
+int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation);
+int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out);
+int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component);
+int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component);
+int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
+int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp);
+int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len);
+int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote);
+int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp);
+int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *local, nr_ice_candidate *remote, nr_ice_cand_pair **pair);
+int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote);
+int
+nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr);
+int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int component_id, int *can_send, struct timeval *ts);
+int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id);
+int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand);
+void nr_ice_media_stream_role_change(nr_ice_media_stream *stream);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_parser.c b/media/mtransport/third_party/nICEr/src/ice/ice_parser.c
new file mode 100644
index 000000000..7ec2bec5b
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_parser.c
@@ -0,0 +1,575 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_parser.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <csi_platform.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <strings.h>
+#endif
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "nr_api.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_reg.h"
+
+static void
+skip_whitespace(char **str)
+{
+ char *c = *str;
+ while (*c == ' ')
+ ++c;
+
+ *str = c;
+}
+
+static void
+fast_forward(char **str, int skip)
+{
+ char *c = *str;
+ while (*c != '\0' && skip-- > 0)
+ ++c;
+
+ *str = c;
+}
+
+static void
+skip_to_past_space(char **str)
+{
+ char *c = *str;
+ while (*c != ' ' && *c != '\0')
+ ++c;
+
+ *str = c;
+
+ skip_whitespace(str);
+}
+
+static int
+grab_token(char **str, char **out)
+{
+ int _status;
+ char *c = *str;
+ int len;
+ char *tmp;
+
+ while (*c != ' ' && *c != '\0')
+ ++c;
+
+ len = c - *str;
+
+ tmp = RMALLOC(len + 1);
+ if (!tmp)
+ ABORT(R_NO_MEMORY);
+
+ memcpy(tmp, *str, len);
+ tmp[len] = '\0';
+
+ *str = c;
+ *out = tmp;
+
+ _status = 0;
+abort:
+ return _status;
+}
+
+int
+nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *orig,nr_ice_media_stream *stream,nr_ice_candidate **candp)
+{
+ int r,_status;
+ char* str = orig;
+ nr_ice_candidate *cand;
+ char *connection_address=0;
+ unsigned int port;
+ int i;
+ unsigned int component_id;
+ char *rel_addr=0;
+ unsigned char transport;
+
+ if(!(cand=RCALLOC(sizeof(nr_ice_candidate))))
+ ABORT(R_NO_MEMORY);
+
+ if(!(cand->label=r_strdup(orig)))
+ ABORT(R_NO_MEMORY);
+
+ cand->ctx=ctx;
+ cand->isock=0;
+ cand->state=NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED;
+ cand->stream=stream;
+ skip_whitespace(&str);
+
+ /* Skip a= if present */
+ if (!strncmp(str, "a=", 2))
+ str += 2;
+
+ /* Candidate attr */
+ if (strncasecmp(str, "candidate:", 10))
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, 10);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ /* Foundation */
+ if ((r=grab_token(&str, &cand->foundation)))
+ ABORT(r);
+
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ /* component */
+ if (sscanf(str, "%u", &component_id) != 1)
+ ABORT(R_BAD_DATA);
+
+ if (component_id < 1 || component_id > 256)
+ ABORT(R_BAD_DATA);
+
+ cand->component_id = (UCHAR)component_id;
+
+ skip_to_past_space(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ /* Protocol */
+ if (!strncasecmp(str, "UDP", 3))
+ transport=IPPROTO_UDP;
+ else if (!strncasecmp(str, "TCP", 3))
+ transport=IPPROTO_TCP;
+ else
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, 3);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ /* priority */
+ if (sscanf(str, "%u", &cand->priority) != 1)
+ ABORT(R_BAD_DATA);
+
+ if (cand->priority < 1)
+ ABORT(R_BAD_DATA);
+
+ skip_to_past_space(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ /* Peer address/port */
+ if ((r=grab_token(&str, &connection_address)))
+ ABORT(r);
+
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if (sscanf(str, "%u", &port) != 1)
+ ABORT(R_BAD_DATA);
+
+ if (port < 1 || port > 0x0FFFF)
+ ABORT(R_BAD_DATA);
+
+ if ((r=nr_str_port_to_transport_addr(connection_address,port,transport,&cand->addr)))
+ ABORT(r);
+
+ skip_to_past_space(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ /* Type */
+ if (strncasecmp("typ", str, 3))
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, 3);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ assert(nr_ice_candidate_type_names[0] == 0);
+
+ for (i = 1; nr_ice_candidate_type_names[i]; ++i) {
+ if(!strncasecmp(nr_ice_candidate_type_names[i], str, strlen(nr_ice_candidate_type_names[i]))) {
+ cand->type=i;
+ break;
+ }
+ }
+ if (nr_ice_candidate_type_names[i] == 0)
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, strlen(nr_ice_candidate_type_names[i]));
+
+ /* Look for the other side's raddr, rport */
+ /* raddr, rport */
+ switch (cand->type) {
+ case HOST:
+ break;
+ case SERVER_REFLEXIVE:
+ case PEER_REFLEXIVE:
+ case RELAYED:
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if (strncasecmp("raddr", str, 5))
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, 5);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if ((r=grab_token(&str, &rel_addr)))
+ ABORT(r);
+
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if (strncasecmp("rport", str, 5))
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, 5);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if (sscanf(str, "%u", &port) != 1)
+ ABORT(R_BAD_DATA);
+
+ if (port > 0x0FFFF)
+ ABORT(R_BAD_DATA);
+
+ if ((r=nr_str_port_to_transport_addr(rel_addr,port,transport,&cand->base)))
+ ABORT(r);
+
+ skip_to_past_space(&str);
+ /* it's expected to be at EOD at this point */
+
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ skip_whitespace(&str);
+
+ if (transport == IPPROTO_TCP && cand->type != RELAYED) {
+ /* Parse tcptype extension per RFC 6544 S 4.5 */
+ if (strncasecmp("tcptype ", str, 8))
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, 8);
+ skip_whitespace(&str);
+
+ for (i = 1; nr_ice_candidate_tcp_type_names[i]; ++i) {
+ if(!strncasecmp(nr_ice_candidate_tcp_type_names[i], str, strlen(nr_ice_candidate_tcp_type_names[i]))) {
+ cand->tcp_type=i;
+ fast_forward(&str, strlen(nr_ice_candidate_tcp_type_names[i]));
+ break;
+ }
+ }
+
+ if (cand->tcp_type == 0)
+ ABORT(R_BAD_DATA);
+
+ if (*str && *str != ' ')
+ ABORT(R_BAD_DATA);
+ }
+ /* Ignore extensions per RFC 5245 S 15.1 */
+#if 0
+ /* This used to be an assert, but we don't want to exit on invalid
+ remote data */
+ if (strlen(str) != 0) {
+ ABORT(R_BAD_DATA);
+ }
+#endif
+
+ nr_ice_candidate_compute_codeword(cand);
+
+ *candp=cand;
+
+ _status=0;
+ abort:
+ if (_status){
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error parsing attribute: %s",ctx->label,orig);
+ nr_ice_candidate_destroy(&cand);
+ }
+
+ RFREE(connection_address);
+ RFREE(rel_addr);
+ return(_status);
+}
+
+
+int
+nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr)
+{
+ int r,_status;
+ char *orig = 0;
+ char *str;
+
+ orig = str = attr;
+
+ if (!strncasecmp(str, "ice-ufrag:", 10)) {
+ fast_forward(&str, 10);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if ((r=grab_token(&str, &stream->ufrag)))
+ ABORT(r);
+ }
+ else if (!strncasecmp(str, "ice-pwd:", 8)) {
+ fast_forward(&str, 8);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if ((r=grab_token(&str, &stream->pwd)))
+ ABORT(r);
+ }
+ else {
+ ABORT(R_BAD_DATA);
+ }
+
+ skip_whitespace(&str);
+
+ /* RFC 5245 grammar doesn't have an extension point for ice-pwd or
+ ice-ufrag: if there's anything left on the line, we treat it as bad. */
+ if (str[0] != '\0') {
+ ABORT(R_BAD_DATA);
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ if (orig)
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig);
+ }
+
+ return(_status);
+}
+
+int
+nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct)
+{
+ int r,_status;
+ int i;
+ char *orig = 0;
+ char *str;
+ char *component_id = 0;
+ char *connection_address = 0;
+ unsigned int port;
+ in_addr_t addr;
+ char *ice_option_tag = 0;
+
+ for(i=0;i<attr_ct;i++){
+ orig = str = attrs[i];
+
+ component_id = 0;
+ connection_address = 0;
+ ice_option_tag = 0;
+
+ if (!strncasecmp(str, "remote-candidates:", 18)) {
+ fast_forward(&str, 18);
+ skip_whitespace(&str);
+
+ while (*str != '\0') {
+ if ((r=grab_token(&str, &component_id)))
+ ABORT(r);
+
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if ((r=grab_token(&str, &connection_address)))
+ ABORT(r);
+
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ addr = inet_addr(connection_address);
+ if (addr == INADDR_NONE)
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ if (sscanf(str, "%u", &port) != 1)
+ ABORT(R_BAD_DATA);
+
+ if (port < 1 || port > 0x0FFFF)
+ ABORT(R_BAD_DATA);
+
+ skip_to_past_space(&str);
+
+#if 0
+ /* TODO: !nn! just drop on the floor for now, later put somewhere */
+ /* Assume v4 for now */
+ if(r=nr_ip4_port_to_transport_addr(ntohl(addr),port,IPPROTO_UDP,&candidate->base))
+ ABORT(r);
+
+ TAILQ_INSERT_TAIL(head, elm, field);
+#endif
+
+ component_id = 0; /* prevent free */
+ RFREE(connection_address);
+ connection_address = 0; /* prevent free */
+ }
+ }
+ else if (!strncasecmp(str, "ice-lite", 8)) {
+ pctx->peer_lite = 1;
+
+ fast_forward(&str, 8);
+ }
+ else if (!strncasecmp(str, "ice-mismatch", 12)) {
+ pctx->peer_ice_mismatch = 1;
+
+ fast_forward(&str, 12);
+ }
+ else if (!strncasecmp(str, "ice-ufrag:", 10)) {
+ fast_forward(&str, 10);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ RFREE(pctx->peer_ufrag);
+ pctx->peer_ufrag = 0;
+ if ((r=grab_token(&str, &pctx->peer_ufrag)))
+ ABORT(r);
+ }
+ else if (!strncasecmp(str, "ice-pwd:", 8)) {
+ fast_forward(&str, 8);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ skip_whitespace(&str);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+
+ RFREE(pctx->peer_pwd);
+ pctx->peer_pwd = 0;
+ if ((r=grab_token(&str, &pctx->peer_pwd)))
+ ABORT(r);
+ }
+ else if (!strncasecmp(str, "ice-options:", 12)) {
+ fast_forward(&str, 12);
+ skip_whitespace(&str);
+
+ while (*str != '\0') {
+ if ((r=grab_token(&str, &ice_option_tag)))
+ ABORT(r);
+
+ skip_whitespace(&str);
+
+ //TODO: for now, just throw away; later put somewhere
+ RFREE(ice_option_tag);
+
+ ice_option_tag = 0; /* prevent free */
+ }
+ }
+ else {
+ ABORT(R_BAD_DATA);
+ }
+
+ skip_whitespace(&str);
+
+ /* RFC 5245 grammar doesn't have an extension point for any of the
+ preceding attributes: if there's anything left on the line, we
+ treat it as bad data. */
+ if (str[0] != '\0') {
+ ABORT(R_BAD_DATA);
+ }
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ if (orig)
+ r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig);
+ }
+
+ RFREE(connection_address);
+ RFREE(component_id);
+ RFREE(ice_option_tag);
+ return(_status);
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
new file mode 100644
index 000000000..d494e45de
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
@@ -0,0 +1,825 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_peer_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <string.h>
+#include <assert.h>
+#include <registry.h>
+#include <nr_api.h>
+#include "ice_ctx.h"
+#include "ice_peer_ctx.h"
+#include "ice_media_stream.h"
+#include "ice_util.h"
+#include "nr_crypto.h"
+#include "async_timer.h"
+#include "ice_reg.h"
+
+static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg);
+static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct);
+static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate);
+
+int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp)
+ {
+ int r,_status;
+ nr_ice_peer_ctx *pctx=0;
+
+ if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ pctx->state = NR_ICE_PEER_STATE_UNPAIRED;
+
+ if(!(pctx->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+
+ pctx->ctx=ctx;
+ pctx->handler=handler;
+
+ /* Decide controlling vs. controlled */
+ if(ctx->flags & NR_ICE_CTX_FLAGS_LITE){
+ pctx->controlling=0;
+ }
+ else{
+ if(ctx->flags & NR_ICE_CTX_FLAGS_OFFERER)
+ pctx->controlling=1;
+ else if(ctx->flags & NR_ICE_CTX_FLAGS_ANSWERER)
+ pctx->controlling=0;
+ }
+ if(r=nr_crypto_random_bytes((UCHAR *)&pctx->tiebreaker,8))
+ ABORT(r);
+
+ STAILQ_INIT(&pctx->peer_streams);
+
+ STAILQ_INSERT_TAIL(&ctx->peers,pctx,entry);
+
+ *pctxp=pctx;
+
+ _status = 0;
+ abort:
+ if(_status){
+ nr_ice_peer_ctx_destroy_cb(0,0,pctx);
+ }
+ return(_status);
+ }
+
+
+
+int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct)
+ {
+ nr_ice_media_stream *pstream=0;
+ nr_ice_component *comp,*comp2;
+ char *lufrag,*rufrag;
+ char *lpwd,*rpwd;
+ int r,_status;
+
+ /*
+ Note: use component_ct from our own stream since components other
+ than this offered by the other side are unusable */
+ if(r=nr_ice_media_stream_create(pctx->ctx,stream->label,stream->component_ct,&pstream))
+ ABORT(r);
+
+ /* Match up the local and remote components */
+ comp=STAILQ_FIRST(&stream->components);
+ comp2=STAILQ_FIRST(&pstream->components);
+ while(comp){
+ comp2->local_component=comp;
+
+ comp=STAILQ_NEXT(comp,entry);
+ comp2=STAILQ_NEXT(comp2,entry);
+ }
+
+ pstream->local_stream=stream;
+ pstream->pctx=pctx;
+
+ if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct))
+ ABORT(r);
+
+ /* Now that we have the ufrag and password, compute all the username/password
+ pairs */
+ lufrag=stream->ufrag?stream->ufrag:pctx->ctx->ufrag;
+ lpwd=stream->pwd?stream->pwd:pctx->ctx->pwd;
+ assert(lufrag);
+ assert(lpwd);
+ rufrag=pstream->ufrag?pstream->ufrag:pctx->peer_ufrag;
+ rpwd=pstream->pwd?pstream->pwd:pctx->peer_pwd;
+ if (!rufrag || !rpwd)
+ ABORT(R_BAD_DATA);
+
+ if(r=nr_concat_strings(&pstream->r2l_user,lufrag,":",rufrag,NULL))
+ ABORT(r);
+ if(r=nr_concat_strings(&pstream->l2r_user,rufrag,":",lufrag,NULL))
+ ABORT(r);
+ if(r=r_data_make(&pstream->r2l_pass, (UCHAR *)lpwd, strlen(lpwd)))
+ ABORT(r);
+ if(r=r_data_make(&pstream->l2r_pass, (UCHAR *)rpwd, strlen(rpwd)))
+ ABORT(r);
+
+ STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct)
+ {
+ int r;
+ int i;
+
+ for(i=0;i<attr_ct;i++){
+ if(!strncmp(attrs[i],"ice-",4)){
+ if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus ICE attribute",pctx->ctx->label,pctx->label);
+ continue;
+ }
+ }
+ else if (!strncmp(attrs[i],"candidate",9)){
+ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,attrs[i])) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus candidate",pctx->ctx->label,pctx->label);
+ continue;
+ }
+ }
+ else {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus attribute: %s",pctx->ctx->label,pctx->label,attrs[i]);
+ }
+ }
+
+ /* Doesn't fail because we just skip errors */
+ return(0);
+ }
+
+static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate)
+ {
+ nr_ice_candidate *cand=0;
+ nr_ice_component *comp;
+ int j;
+ int r, _status;
+
+ if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,candidate,pstream,&cand))
+ ABORT(r);
+ if(cand->component_id-1>=pstream->component_ct){
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label);
+ ABORT(R_BAD_DATA);
+ }
+
+ /* Not the fastest way to find a component, but it's what we got */
+ j=1;
+ for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){
+ if(j==cand->component_id)
+ break;
+
+ j++;
+ }
+
+ if(!comp){
+ r_log(LOG_ICE,LOG_WARNING,"Peer answered with more components than we offered");
+ ABORT(R_BAD_DATA);
+ }
+
+ if (comp->state == NR_ICE_COMPONENT_DISABLED) {
+ r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled remote component");
+ ABORT(R_BAD_DATA);
+ }
+ if (comp->local_component->state == NR_ICE_COMPONENT_DISABLED) {
+ r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled local component");
+ ABORT(R_BAD_DATA);
+ }
+
+ cand->component=comp;
+
+ TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): creating peer candidate",
+ pctx->label,cand->label);
+
+ _status=0;
+ abort:
+ if (_status) {
+ nr_ice_candidate_destroy(&cand);
+ }
+ return(_status);
+ }
+
+int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp)
+ {
+ int _status;
+ nr_ice_media_stream *pstream;
+
+ /* Because we don't have forward pointers, iterate through all the
+ peer streams to find one that matches us */
+ pstream=STAILQ_FIRST(&pctx->peer_streams);
+ while(pstream) {
+ if (pstream->local_stream == stream)
+ break;
+
+ pstream = STAILQ_NEXT(pstream, entry);
+ }
+ if (!pstream) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label);
+ ABORT(R_NOT_FOUND);
+ }
+
+ *pstreamp = pstream;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp)
+ {
+ int r,_status;
+
+ STAILQ_REMOVE(&pctx->peer_streams,*pstreamp,nr_ice_media_stream_,entry);
+
+ if(r=nr_ice_media_stream_destroy(pstreamp)) {
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate)
+ {
+ nr_ice_media_stream *pstream;
+ int r,_status;
+ int needs_pairing = 0;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) parsing trickle ICE candidate %s",pctx->ctx->label,pctx->label,candidate);
+ r = nr_ice_peer_ctx_find_pstream(pctx, stream, &pstream);
+ if (r)
+ ABORT(r);
+
+ switch(pstream->ice_state) {
+ case NR_ICE_MEDIA_STREAM_UNPAIRED:
+ break;
+ case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN:
+ case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE:
+ needs_pairing = 1;
+ break;
+ default:
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) tried to trickle ICE in inappropriate state %d",pctx->ctx->label,pctx->label,stream->label,pstream->ice_state);
+ ABORT(R_ALREADY);
+ break;
+ }
+
+ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,candidate)){
+ ABORT(r);
+ }
+
+ /* If ICE is running (i.e., we are in FROZEN or ACTIVE states)
+ then we need to pair this new candidate. For now we
+ just re-pair the stream which is inefficient but still
+ fine because we suppress duplicate pairing */
+ if (needs_pairing) {
+ if(r=nr_ice_media_stream_pair_candidates(pctx, stream, pstream)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to pair trickle ICE candidates",pctx->ctx->label,pctx->label,stream->label);
+ ABORT(r);
+ }
+
+ /* Start checks if this stream is not checking yet or if it has checked
+ all the available candidates but not had a completed check for all
+ components.
+
+ Note that this is not compliant with RFC 5245, but consistent with
+ the libjingle trickle ICE behavior. Note that we will not restart
+ checks if either (a) the stream has failed or (b) all components
+ have a successful pair because the switch statement above jumps
+ will in both states.
+
+ TODO(ekr@rtfm.com): restart checks.
+ TODO(ekr@rtfm.com): update when the trickle ICE RFC is published
+ */
+ if (!pstream->timer) {
+ if(r=nr_ice_media_stream_start_checks(pctx, pstream)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to start checks",pctx->ctx->label,pctx->label,stream->label);
+ ABORT(r);
+ }
+ }
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+
+ }
+
+
+static void nr_ice_peer_ctx_trickle_wait_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_peer_ctx *pctx=cb_arg;
+ nr_ice_media_stream *stream;
+ nr_ice_component *comp;
+
+ pctx->trickle_grace_period_timer=0;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) Trickle grace period is over; marking every component with only failed pairs as failed.",pctx->ctx->label,pctx->label);
+
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ while(stream){
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ nr_ice_component_check_if_failed(comp);
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ stream=STAILQ_NEXT(stream,entry);
+ }
+ }
+
+static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx)
+ {
+ UINT4 grace_period_timeout=0;
+
+ if(pctx->trickle_grace_period_timer) {
+ NR_async_timer_cancel(pctx->trickle_grace_period_timer);
+ pctx->trickle_grace_period_timer=0;
+ }
+
+ NR_reg_get_uint4(NR_ICE_REG_TRICKLE_GRACE_PERIOD,&grace_period_timeout);
+
+ if (grace_period_timeout) {
+ /* If we're doing trickle, we need to allow a grace period for new
+ * trickle candidates to arrive in case the pairs we have fail quickly. */
+ NR_ASYNC_TIMER_SET(grace_period_timeout,nr_ice_peer_ctx_trickle_wait_cb,pctx,&pctx->trickle_grace_period_timer);
+ }
+ }
+
+int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *stream;
+ int r,_status;
+
+ if(pctx->peer_lite && !pctx->controlling) {
+ if(pctx->ctx->flags & NR_ICE_CTX_FLAGS_LITE){
+ r_log(LOG_ICE,LOG_ERR,"Both sides are ICE-Lite");
+ ABORT(R_BAD_DATA);
+ }
+ nr_ice_peer_ctx_switch_controlling_role(pctx);
+ }
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) pairing candidates",pctx->ctx->label,pctx->label);
+
+ if(STAILQ_EMPTY(&pctx->peer_streams)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attributes",pctx->ctx->label,pctx->label);
+ ABORT(R_FAILED);
+ }
+
+ /* Set this first; if we fail partway through, we do not want to end
+ * up in UNPAIRED after creating some pairs. */
+ pctx->state = NR_ICE_PEER_STATE_PAIRED;
+
+ /* Start grace period timer for incoming trickle candidates */
+ nr_ice_peer_ctx_start_trickle_timer(pctx);
+
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ while(stream){
+ if(r=nr_ice_media_stream_pair_candidates(pctx, stream->local_stream,
+ stream))
+ ABORT(r);
+
+ stream=STAILQ_NEXT(stream,entry);
+ }
+
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand)
+ {
+ int r, _status;
+ nr_ice_media_stream *pstream;
+
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) pairing local trickle ICE candidate %s",pctx->ctx->label,pctx->label,cand->label);
+ if ((r = nr_ice_peer_ctx_find_pstream(pctx, cand->stream, &pstream)))
+ ABORT(r);
+
+ if ((r = nr_ice_media_stream_pair_new_trickle_candidate(pctx, pstream, cand)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return _status;
+ }
+
+int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id)
+ {
+ int r, _status;
+ nr_ice_media_stream *pstream;
+ nr_ice_component *component;
+
+ if ((r=nr_ice_peer_ctx_find_pstream(pctx, lstream, &pstream)))
+ ABORT(r);
+
+ /* We shouldn't be calling this after we have started pairing */
+ if (pstream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED)
+ ABORT(R_FAILED);
+
+ if ((r=nr_ice_media_stream_find_component(pstream, component_id,
+ &component)))
+ ABORT(r);
+
+ component->state = NR_ICE_COMPONENT_DISABLED;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_peer_ctx *pctx=cb_arg;
+ nr_ice_media_stream *str1,*str2;
+
+ NR_async_timer_cancel(pctx->connected_cb_timer);
+ RFREE(pctx->label);
+ RFREE(pctx->peer_ufrag);
+ RFREE(pctx->peer_pwd);
+
+ STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){
+ STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry);
+ nr_ice_media_stream_destroy(&str1);
+ }
+ assert(pctx->ctx);
+ if (pctx->ctx)
+ STAILQ_REMOVE(&pctx->ctx->peers, pctx, nr_ice_peer_ctx_, entry);
+
+ if(pctx->trickle_grace_period_timer) {
+ NR_async_timer_cancel(pctx->trickle_grace_period_timer);
+ pctx->trickle_grace_period_timer=0;
+ }
+
+ RFREE(pctx);
+ }
+
+int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp)
+ {
+
+ if(!pctxp || !*pctxp)
+ return(0);
+
+ /* Stop calling the handler */
+ (*pctxp)->handler = 0;
+
+ NR_ASYNC_SCHEDULE(nr_ice_peer_ctx_destroy_cb,*pctxp);
+
+ *pctxp=0;
+
+ return(0);
+ }
+
+
+/* Start the checks for the first media stream (S 5.7)
+ The rest remain FROZEN */
+int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx)
+ {
+ return nr_ice_peer_ctx_start_checks2(pctx, 0);
+ }
+
+/* Start checks for some media stream.
+
+ If allow_non_first == 0, then we only look at the first stream,
+ which is 5245-complaint.
+
+ If allow_non_first == 1 then we find the first non-empty stream
+ This is not compliant with RFC 5245 but is necessary to make trickle ICE
+ work plausibly
+*/
+int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
+ {
+ int r,_status;
+ nr_ice_media_stream *stream;
+ int started = 0;
+
+ /* Might have added some streams */
+ pctx->reported_connected = 0;
+ NR_async_timer_cancel(pctx->connected_cb_timer);
+ pctx->connected_cb_timer = 0;
+ pctx->checks_started = 0;
+
+ if((r=nr_ice_peer_ctx_check_if_connected(pctx))) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) initial connected check failed",pctx->ctx->label,pctx->label);
+ ABORT(r);
+ }
+
+ if (pctx->reported_connected) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) in %s all streams were done",pctx->ctx->label,pctx->label,__FUNCTION__);
+ return (0);
+ }
+
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ if(!stream)
+ ABORT(R_FAILED);
+
+ while (stream) {
+ assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED);
+
+ if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
+ if(!TAILQ_EMPTY(&stream->check_list))
+ break;
+
+ if(!allow_non_first){
+ /* This test applies if:
+
+ 1. allow_non_first is 0 (i.e., non-trickle ICE)
+ 2. the first stream has an empty check list.
+
+ But in the non-trickle ICE case, the other side should have provided
+ some candidates or ICE is pretty much not going to work and we're
+ just going to fail. Hence R_FAILED as opposed to R_NOT_FOUND and
+ immediate termination here.
+ */
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label);
+ ABORT(R_FAILED);
+ }
+ }
+
+ stream=STAILQ_NEXT(stream, entry);
+ }
+
+ if (!stream) {
+ /*
+ We fail above if we aren't doing trickle, and this is not all that
+ unusual in the trickle case.
+ */
+ r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label);
+ }
+ else if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
+ if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream))
+ ABORT(r);
+ if(r=nr_ice_media_stream_start_checks(pctx,stream))
+ ABORT(r);
+ ++started;
+ }
+
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ while (stream) {
+ int serviced = 0;
+ if (r=nr_ice_media_stream_service_pre_answer_requests(pctx, stream->local_stream, stream, &serviced))
+ ABORT(r);
+
+ if (serviced) {
+ ++started;
+ }
+ else {
+ r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with pre-answer requests",pctx->ctx->label,pctx->label);
+ }
+
+
+ stream=STAILQ_NEXT(stream, entry);
+ }
+
+ if (!started) {
+ r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no checks to start",pctx->ctx->label,pctx->label);
+ ABORT(R_NOT_FOUND);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
+ {
+ if (!pctx->checks_started) {
+ r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) is now checking",pctx->ctx->label,pctx->label);
+ pctx->checks_started = 1;
+ if (pctx->handler && pctx->handler->vtbl->ice_checking) {
+ pctx->handler->vtbl->ice_checking(pctx->handler->obj, pctx);
+ }
+ }
+ }
+
+#ifndef NDEBUG
+int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out)
+ {
+ int r,_status;
+ nr_ice_media_stream *stream;
+
+ fprintf(out,"PEER %s STATE DUMP\n",pctx->label);
+ fprintf(out,"==========================================\n");
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ while(stream){
+ if(r=nr_ice_media_stream_dump_state(pctx,stream,out))
+ ABORT(r);
+
+ stream=STAILQ_NEXT(stream,entry);
+ }
+ fprintf(out,"==========================================\n");
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+#endif
+
+void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx)
+ {
+ if (pctx->reported_connected &&
+ pctx->handler &&
+ pctx->handler->vtbl->ice_disconnected) {
+ pctx->handler->vtbl->ice_disconnected(pctx->handler->obj, pctx);
+ }
+ }
+
+void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx)
+ {
+ /* Fire the handler callback to say we're done */
+ if (pctx->reported_connected &&
+ pctx->handler &&
+ pctx->handler->vtbl->ice_connected) {
+ pctx->handler->vtbl->ice_connected(pctx->handler->obj, pctx);
+ }
+ }
+
+static void nr_ice_peer_ctx_fire_connected(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_peer_ctx *pctx=cb_arg;
+
+ pctx->connected_cb_timer=0;
+
+ nr_ice_peer_ctx_connected(pctx);
+ }
+
+/* Examine all the streams to see if we're
+ maybe miraculously connected */
+int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx)
+ {
+ int _status;
+ nr_ice_media_stream *str;
+ int failed=0;
+ int succeeded=0;
+
+ str=STAILQ_FIRST(&pctx->peer_streams);
+ while(str){
+ if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED){
+ succeeded++;
+ }
+ else if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FAILED){
+ failed++;
+ }
+ else{
+ break;
+ }
+ str=STAILQ_NEXT(str,entry);
+ }
+
+ if(str)
+ goto done; /* Something isn't done */
+
+ /* OK, we're finished, one way or another */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): all checks completed success=%d fail=%d",pctx->label,succeeded,failed);
+
+ /* Schedule a connected notification for the first connected event.
+ IMPORTANT: This is done in a callback because we expect destructors
+ of various kinds to be fired from here */
+ if (!pctx->reported_connected) {
+ pctx->reported_connected = 1;
+ assert(!pctx->connected_cb_timer);
+ NR_ASYNC_TIMER_SET(0,nr_ice_peer_ctx_fire_connected,pctx,&pctx->connected_cb_timer);
+ }
+
+ done:
+ _status=0;
+// abort:
+ return(_status);
+ }
+
+
+/* Given a component in the main ICE ctx, find the relevant component in
+ the peer_ctx */
+int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp)
+ {
+ nr_ice_media_stream *pstr;
+ int r,_status;
+
+ pstr=STAILQ_FIRST(&pctx->peer_streams);
+ while(pstr){
+ if(pstr->local_stream==str)
+ break;
+
+ pstr=STAILQ_NEXT(pstr,entry);
+ }
+ if(!pstr)
+ ABORT(R_BAD_ARGS);
+
+ if(r=nr_ice_media_stream_find_component(pstr,component_id,compp))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/*
+ This packet may be for us.
+
+ 1. Find the matching peer component
+ 2. Examine the packet source address to see if it matches
+ one of the peer candidates.
+ 3. Fire the relevant callback handler if there is a match
+
+ Return 0 if match, R_REJECTED if no match, other errors
+ if we can't even find the component or something like that.
+*/
+
+int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len)
+ {
+ nr_ice_component *peer_comp;
+ nr_ice_candidate *cand;
+ int r,_status;
+
+ if(r=nr_ice_peer_ctx_find_component(pctx, comp->stream, comp->component_id,
+ &peer_comp))
+ ABORT(r);
+
+ /* OK, we've found the component, now look for matches */
+ cand=TAILQ_FIRST(&peer_comp->candidates);
+ while(cand){
+ if(!nr_transport_addr_cmp(source_addr,&cand->addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ break;
+
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+
+ if(!cand)
+ ABORT(R_REJECTED);
+
+ /* OK, there's a match. Call the handler */
+
+ if (pctx->handler) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label);
+
+ pctx->handler->vtbl->msg_recvd(pctx->handler->obj,
+ pctx,comp->stream,comp->component_id,data,len);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx)
+ {
+ int controlling = !(pctx->controlling);
+ if(pctx->controlling_conflict_resolved) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) %s called more than once; "
+ "this probably means the peer is confused. Not switching roles.",
+ pctx->ctx->label,pctx->label,__FUNCTION__);
+ return;
+ }
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): detected "
+ "role conflict. Switching to %s",
+ pctx->label,
+ controlling ? "controlling" : "controlled");
+
+ pctx->controlling = controlling;
+ pctx->controlling_conflict_resolved = 1;
+
+ if(pctx->state == NR_ICE_PEER_STATE_PAIRED) {
+ /* We have formed candidate pairs. We need to inform them. */
+ nr_ice_media_stream *pstream=STAILQ_FIRST(&pctx->peer_streams);
+ while(pstream) {
+ nr_ice_media_stream_role_change(pstream);
+ pstream = STAILQ_NEXT(pstream, entry);
+ }
+ }
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
new file mode 100644
index 000000000..47e3be704
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
@@ -0,0 +1,101 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_peer_ctx_h
+#define _ice_peer_ctx_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+struct nr_ice_peer_ctx_ {
+ int state;
+#define NR_ICE_PEER_STATE_UNPAIRED 1
+#define NR_ICE_PEER_STATE_PAIRED 2
+
+ char *label;
+ nr_ice_ctx *ctx;
+ nr_ice_handler *handler;
+
+ UCHAR controlling; /* 1 for controlling, 0 for controlled */
+ UCHAR controlling_conflict_resolved;
+ UINT8 tiebreaker;
+
+ char *peer_ufrag;
+ char *peer_pwd;
+ int peer_lite;
+ int peer_ice_mismatch;
+
+ nr_ice_media_stream_head peer_streams;
+ int active_streams;
+ int waiting_pairs;
+ UCHAR checks_started;
+
+ void *connected_cb_timer;
+ UCHAR reported_connected;
+ void *trickle_grace_period_timer;
+
+ STAILQ_ENTRY(nr_ice_peer_ctx_) entry;
+};
+
+typedef STAILQ_HEAD(nr_ice_peer_ctx_head_, nr_ice_peer_ctx_) nr_ice_peer_ctx_head;
+
+int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp);
+int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp);
+int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct);
+int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp);
+int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp);
+int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand);
+
+int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx);
+int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct);
+int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx);
+int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first);
+void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx);
+void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx);
+int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out);
+int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx);
+int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx);
+int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp);
+int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
+int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id);
+int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand);
+void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_reg.h b/media/mtransport/third_party/nICEr/src/ice/ice_reg.h
new file mode 100644
index 000000000..dc0f74172
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_reg.h
@@ -0,0 +1,79 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_reg_h
+#define _ice_reg_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+#define NR_ICE_REG_PREF_TYPE_HOST "ice.pref.type.host"
+#define NR_ICE_REG_PREF_TYPE_RELAYED "ice.pref.type.relayed"
+#define NR_ICE_REG_PREF_TYPE_SRV_RFLX "ice.pref.type.srv_rflx"
+#define NR_ICE_REG_PREF_TYPE_PEER_RFLX "ice.pref.type.peer_rflx"
+#define NR_ICE_REG_PREF_TYPE_HOST_TCP "ice.pref.type.host_tcp"
+#define NR_ICE_REG_PREF_TYPE_RELAYED_TCP "ice.pref.type.relayed_tcp"
+#define NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP "ice.pref.type.srv_rflx_tcp"
+#define NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP "ice.pref.type.peer_rflx_tcp"
+
+#define NR_ICE_REG_PREF_INTERFACE_PRFX "ice.pref.interface"
+#define NR_ICE_REG_SUPPRESS_INTERFACE_PRFX "ice.suppress.interface"
+
+#define NR_ICE_REG_STUN_SRV_PRFX "ice.stun.server"
+#define NR_ICE_REG_STUN_SRV_ADDR "addr"
+#define NR_ICE_REG_STUN_SRV_PORT "port"
+
+#define NR_ICE_REG_TURN_SRV_PRFX "ice.turn.server"
+#define NR_ICE_REG_TURN_SRV_ADDR "addr"
+#define NR_ICE_REG_TURN_SRV_PORT "port"
+#define NR_ICE_REG_TURN_SRV_BANDWIDTH "bandwidth"
+#define NR_ICE_REG_TURN_SRV_LIFETIME "lifetime"
+#define NR_ICE_REG_TURN_SRV_USERNAME "username"
+#define NR_ICE_REG_TURN_SRV_PASSWORD "password"
+
+#define NR_ICE_REG_ICE_TCP_DISABLE "ice.tcp.disable"
+#define NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT "ice.tcp.so_sock_count"
+#define NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG "ice.tcp.listen_backlog"
+
+#define NR_ICE_REG_KEEPALIVE_TIMER "ice.keepalive_timer"
+
+#define NR_ICE_REG_TRICKLE_GRACE_PERIOD "ice.trickle_grace_period"
+#define NR_ICE_REG_PREF_FORCE_INTERFACE_NAME "ice.forced_interface_name"
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
new file mode 100644
index 000000000..f9251a280
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
@@ -0,0 +1,380 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_socket.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+#include <assert.h>
+#include <string.h>
+#include "nr_api.h"
+#include "ice_ctx.h"
+#include "stun.h"
+#include "nr_socket_buffered_stun.h"
+#include "nr_socket_multi_tcp.h"
+
+static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ int r;
+ nr_ice_stun_ctx *sc1,*sc2;
+ nr_ice_socket *sock=cb_arg;
+ UCHAR buf[9216];
+ char string[256];
+ nr_transport_addr addr;
+ int len;
+ size_t len_s;
+ int is_stun;
+ int is_req;
+ int is_ind;
+ int processed_indication=0;
+
+ nr_socket *stun_srv_sock=sock->sock;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label);
+
+ /* Re-arm first! */
+ if (sock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): rearming",sock->ctx->label);
+ NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg);
+ }
+
+ if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){
+ if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) {
+ /* Report this error upward. Bug 946423 */
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error %d on reliable socket(%p). Abandoning.",sock->ctx->label, r, s);
+ }
+ return;
+ }
+
+ /* Deal with the fact that sizeof(int) and sizeof(size_t) may not
+ be the same */
+ if (len_s > (size_t)INT_MAX)
+ return;
+
+ len = (int)len_s;
+
+#ifdef USE_TURN
+ re_process:
+#endif /* USE_TURN */
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Read %d bytes %sfrom %s",sock->ctx->label,len,(processed_indication ? "relayed " : ""),addr.as_string);
+
+ /* First question: is this STUN or not? */
+ is_stun=nr_is_stun_message(buf,len);
+
+ if(is_stun){
+ is_req=nr_is_stun_request_message(buf,len);
+ is_ind=is_req?0:nr_is_stun_indication_message(buf,len);
+
+ snprintf(string, sizeof(string)-1, "ICE(%s): Message is STUN (%s)",sock->ctx->label,
+ is_req ? "request" : (is_ind ? "indication" : "other"));
+ r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)buf, len);
+
+
+ /* We need to offer it to all of our stun contexts
+ to see who bites */
+ sc1=TAILQ_FIRST(&sock->stun_ctxs);
+ while(sc1){
+ sc2=TAILQ_NEXT(sc1,entry);
+
+ r=-1;
+ switch(sc1->type){
+ /* This has been deleted, prune... */
+ case NR_ICE_STUN_NONE:
+ TAILQ_REMOVE(&sock->stun_ctxs,sc1,entry);
+ RFREE(sc1);
+ break;
+
+ case NR_ICE_STUN_CLIENT:
+ if(!(is_req||is_ind)){
+ r=nr_stun_client_process_response(sc1->u.client,buf,len,&addr);
+ }
+ break;
+
+ case NR_ICE_STUN_SERVER:
+ if(is_req){
+ r=nr_stun_server_process_request(sc1->u.server,stun_srv_sock,(char *)buf,len,&addr,NR_STUN_AUTH_RULE_SHORT_TERM);
+ }
+ break;
+#ifdef USE_TURN
+ case NR_ICE_TURN_CLIENT:
+ /* data indications are ok, so don't ignore those */
+ /* Check that this is from the right TURN server address. Else
+ skip */
+ if (nr_transport_addr_cmp(
+ &sc1->u.turn_client.turn_client->turn_server_addr,
+ &addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ break;
+
+ if(!is_req){
+ if(!is_ind)
+ r=nr_turn_client_process_response(sc1->u.turn_client.turn_client,buf,len,&addr);
+ else{
+ nr_transport_addr n_addr;
+ size_t n_len;
+
+ if (processed_indication) {
+ /* Don't allow recursively wrapped indications */
+ r_log(LOG_ICE, LOG_WARNING,
+ "ICE(%s): discarding recursively wrapped indication",
+ sock->ctx->label);
+ break;
+ }
+ /* This is a bit of a hack. If it's a data indication, strip
+ off the TURN framing and re-enter. This works because
+ all STUN processing is on the same physical socket.
+ We don't care about other kinds of indication */
+ r=nr_turn_client_parse_data_indication(
+ sc1->u.turn_client.turn_client, &addr,
+ buf, len, buf, &n_len, len, &n_addr);
+ if(!r){
+ r_log(LOG_ICE,LOG_DEBUG,"Unwrapped a data indication.");
+ len=n_len;
+ nr_transport_addr_copy(&addr,&n_addr);
+ stun_srv_sock=sc1->u.turn_client.turn_sock;
+ processed_indication=1;
+ goto re_process;
+ }
+ }
+ }
+ break;
+#endif /* USE_TURN */
+
+ default:
+ assert(0); /* Can't happen */
+ return;
+ }
+ if(!r) {
+ break;
+ }
+
+ sc1=sc2;
+ }
+ if(!sc1){
+ if (nr_ice_ctx_is_known_id(sock->ctx,((nr_stun_message_header*)buf)->id.octet))
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is a retransmit",sock->ctx->label);
+ else
+ r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): Message does not correspond to any registered stun ctx",sock->ctx->label);
+ }
+ }
+ else{
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is not STUN",sock->ctx->label);
+
+ nr_ice_ctx_deliver_packet(sock->ctx, sock->component, &addr, buf, len);
+ }
+
+ return;
+ }
+
+int nr_ice_socket_create(nr_ice_ctx *ctx,nr_ice_component *comp, nr_socket *nsock, int type, nr_ice_socket **sockp)
+ {
+ nr_ice_socket *sock=0;
+ NR_SOCKET fd;
+ nr_transport_addr addr;
+ int r,_status;
+
+ if(!(sock=RCALLOC(sizeof(nr_ice_socket))))
+ ABORT(R_NO_MEMORY);
+
+ sock->sock=nsock;
+ sock->ctx=ctx;
+ sock->component=comp;
+
+ if(r=nr_socket_getaddr(nsock, &addr))
+ ABORT(r);
+
+ if (type == NR_ICE_SOCKET_TYPE_DGRAM) {
+ assert(addr.protocol == IPPROTO_UDP);
+ }
+ else {
+ assert(addr.protocol == IPPROTO_TCP);
+ }
+ sock->type=type;
+
+ TAILQ_INIT(&sock->candidates);
+ TAILQ_INIT(&sock->stun_ctxs);
+
+ if (sock->type == NR_ICE_SOCKET_TYPE_DGRAM){
+ if((r=nr_socket_getfd(nsock,&fd)))
+ ABORT(r);
+ NR_ASYNC_WAIT(fd,NR_ASYNC_WAIT_READ,nr_ice_socket_readable_cb,sock);
+ }
+ else if (sock->type == NR_ICE_SOCKET_TYPE_STREAM_TURN) {
+ /* some OS's (e.g. Linux) don't like to see un-connected TCP sockets in
+ * the poll socket set. */
+ nr_socket_buffered_stun_set_readable_cb(nsock,nr_ice_socket_readable_cb,sock);
+ }
+ else if (sock->type == NR_ICE_SOCKET_TYPE_STREAM_TCP) {
+ /* in this case we can't hook up using NR_ASYNC_WAIT, because nr_socket_multi_tcp
+ consists of multiple nr_sockets and file descriptors. */
+ if((r=nr_socket_multi_tcp_set_readable_cb(nsock,nr_ice_socket_readable_cb,sock)))
+ ABORT(r);
+ }
+
+ *sockp=sock;
+
+ _status=0;
+ abort:
+ if(_status) RFREE(sock);
+ return(_status);
+ }
+
+
+int nr_ice_socket_destroy(nr_ice_socket **isockp)
+ {
+ nr_ice_stun_ctx *s1,*s2;
+ nr_ice_socket *isock;
+
+ if(!isockp || !*isockp)
+ return(0);
+
+ isock=*isockp;
+ *isockp=0;
+
+ /* Close the socket */
+ nr_ice_socket_close(isock);
+
+ /* The STUN server */
+ nr_stun_server_ctx_destroy(&isock->stun_server);
+
+ /* Now clean up the STUN ctxs */
+ TAILQ_FOREACH_SAFE(s1, &isock->stun_ctxs, entry, s2){
+ TAILQ_REMOVE(&isock->stun_ctxs, s1, entry);
+ RFREE(s1);
+ }
+
+ RFREE(isock);
+
+ return(0);
+ }
+
+int nr_ice_socket_close(nr_ice_socket *isock)
+ {
+#ifdef NR_SOCKET_IS_VOID_PTR
+ NR_SOCKET fd=NULL;
+ NR_SOCKET no_socket = NULL;
+#else
+ NR_SOCKET fd=-1;
+ NR_SOCKET no_socket = -1;
+#endif
+
+ if (!isock||!isock->sock)
+ return(0);
+
+ if (isock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP){
+ nr_socket_getfd(isock->sock,&fd);
+ assert(isock->sock!=0);
+ if(fd != no_socket){
+ NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_READ);
+ NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_WRITE);
+ }
+ }
+ nr_socket_destroy(&isock->sock);
+
+ return(0);
+ }
+
+int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle)
+ {
+ nr_ice_stun_ctx *sc=0;
+ int _status;
+
+ if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ sc->type=NR_ICE_STUN_CLIENT;
+ sc->u.client=srv;
+
+ TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry);
+
+ *handle=sc;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_socket_register_stun_server(nr_ice_socket *sock, nr_stun_server_ctx *srv,void **handle)
+ {
+ nr_ice_stun_ctx *sc=0;
+ int _status;
+
+ if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ sc->type=NR_ICE_STUN_SERVER;
+ sc->u.server=srv;
+
+ TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry);
+
+ *handle=sc;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ice_socket_register_turn_client(nr_ice_socket *sock, nr_turn_client_ctx *srv,
+ nr_socket *turn_socket, void **handle)
+ {
+ nr_ice_stun_ctx *sc=0;
+ int _status;
+
+ if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ sc->type=NR_ICE_TURN_CLIENT;
+ sc->u.turn_client.turn_client=srv;
+ sc->u.turn_client.turn_sock=turn_socket;
+
+ TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry);
+
+ *handle=sc;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* Just mark it deregistered. Don't delete it now because it's not safe
+ in the CB, which is where this is likely to be called */
+int nr_ice_socket_deregister(nr_ice_socket *sock, void *handle)
+ {
+ nr_ice_stun_ctx *sc=handle;
+
+ if(!sc)
+ return(0);
+
+ sc->type=NR_ICE_STUN_NONE;
+
+ return(0);
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_socket.h b/media/mtransport/third_party/nICEr/src/ice/ice_socket.h
new file mode 100644
index 000000000..b0fba1e1d
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.h
@@ -0,0 +1,97 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_socket_h
+#define _ice_socket_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct nr_ice_stun_ctx_ {
+ int type;
+#define NR_ICE_STUN_NONE 0 /* Deregistered */
+#define NR_ICE_STUN_CLIENT 1
+#define NR_ICE_STUN_SERVER 2
+#define NR_ICE_TURN_CLIENT 3
+
+ union {
+ nr_stun_client_ctx *client;
+ nr_stun_server_ctx *server;
+ struct {
+ nr_turn_client_ctx *turn_client;
+ nr_socket *turn_sock; /* The nr_socket_turn wrapped around
+ turn_client */
+ } turn_client;
+ } u;
+
+ TAILQ_ENTRY(nr_ice_stun_ctx_) entry;
+} nr_ice_stun_ctx;
+
+
+typedef struct nr_ice_socket_ {
+ int type;
+#define NR_ICE_SOCKET_TYPE_DGRAM 1
+#define NR_ICE_SOCKET_TYPE_STREAM_TURN 2
+#define NR_ICE_SOCKET_TYPE_STREAM_TCP 3
+
+ nr_socket *sock;
+ nr_ice_ctx *ctx;
+
+ nr_ice_candidate_head candidates;
+ nr_ice_component *component;
+
+ TAILQ_HEAD(nr_ice_stun_ctx_head_,nr_ice_stun_ctx_) stun_ctxs;
+
+ nr_stun_server_ctx *stun_server;
+ void *stun_server_handle;
+
+ STAILQ_ENTRY(nr_ice_socket_) entry;
+} nr_ice_socket;
+
+typedef STAILQ_HEAD(nr_ice_socket_head_,nr_ice_socket_) nr_ice_socket_head;
+
+int nr_ice_socket_create(struct nr_ice_ctx_ *ctx, struct nr_ice_component_ *comp, nr_socket *nsock, int type, nr_ice_socket **sockp);
+int nr_ice_socket_destroy(nr_ice_socket **isock);
+int nr_ice_socket_close(nr_ice_socket *isock);
+int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle);
+int nr_ice_socket_register_stun_server(nr_ice_socket *sock, nr_stun_server_ctx *srv,void **handle);
+int nr_ice_socket_register_turn_client(nr_ice_socket *sock, nr_turn_client_ctx *srv,nr_socket *turn_socket, void **handle);
+int nr_ice_socket_deregister(nr_ice_socket *sock, void *handle);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/net/local_addr.c b/media/mtransport/third_party/nICEr/src/net/local_addr.c
new file mode 100644
index 000000000..c251f2215
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/local_addr.c
@@ -0,0 +1,61 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <nr_api.h>
+#include <string.h>
+#include "local_addr.h"
+
+int nr_local_addr_copy(nr_local_addr *to, nr_local_addr *from)
+ {
+ nr_transport_addr_copy(&(to->addr), &(from->addr));
+ to->interface = from->interface;
+ return(0);
+ }
+
+int nr_local_addr_fmt_info_string(nr_local_addr *addr, char *buf, int len)
+ {
+ int addr_type = addr->interface.type;
+ const char *vpn = (addr_type & NR_INTERFACE_TYPE_VPN) ? "VPN on " : "";
+
+ const char *type = (addr_type & NR_INTERFACE_TYPE_WIRED) ? "wired" :
+ (addr_type & NR_INTERFACE_TYPE_WIFI) ? "wifi" :
+ (addr_type & NR_INTERFACE_TYPE_MOBILE) ? "mobile" :
+ "unknown";
+
+ snprintf(buf, len, "%s%s, estimated speed: %d kbps",
+ vpn, type, addr->interface.estimated_speed);
+ buf[len - 1] = '\0';
+ return (0);
+ }
diff --git a/media/mtransport/third_party/nICEr/src/net/local_addr.h b/media/mtransport/third_party/nICEr/src/net/local_addr.h
new file mode 100644
index 000000000..90e538856
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/local_addr.h
@@ -0,0 +1,59 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _local_addr_h
+#define _local_addr_h
+
+#include "transport_addr.h"
+
+typedef struct nr_interface_ {
+ int type;
+#define NR_INTERFACE_TYPE_UNKNOWN 0x0
+#define NR_INTERFACE_TYPE_WIRED 0x1
+#define NR_INTERFACE_TYPE_WIFI 0x2
+#define NR_INTERFACE_TYPE_MOBILE 0x4
+#define NR_INTERFACE_TYPE_VPN 0x8
+ int estimated_speed; /* Speed in kbps */
+} nr_interface;
+
+typedef struct nr_local_addr_ {
+ nr_transport_addr addr;
+ nr_interface interface;
+} nr_local_addr;
+
+int nr_local_addr_copy(nr_local_addr *to, nr_local_addr *from);
+int nr_local_addr_fmt_info_string(nr_local_addr *addr, char *buf, int len);
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c
new file mode 100644
index 000000000..75e5f9546
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c
@@ -0,0 +1,88 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "nr_api.h"
+#include "nr_interface_prioritizer.h"
+#include "transport_addr.h"
+
+int nr_interface_prioritizer_create_int(void *obj,
+ nr_interface_prioritizer_vtbl *vtbl,nr_interface_prioritizer **ifpp)
+ {
+ int _status;
+ nr_interface_prioritizer *ifp=0;
+
+ if(!(ifp=RCALLOC(sizeof(nr_interface_prioritizer))))
+ ABORT(R_NO_MEMORY);
+
+ ifp->obj = obj;
+ ifp->vtbl = vtbl;
+
+ *ifpp = ifp;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_interface_prioritizer_destroy(nr_interface_prioritizer **ifpp)
+ {
+ nr_interface_prioritizer *ifp;
+
+ if (!ifpp || !*ifpp)
+ return(0);
+
+ ifp = *ifpp;
+ *ifpp = 0;
+ ifp->vtbl->destroy(&ifp->obj);
+ RFREE(ifp);
+ return(0);
+ }
+
+int nr_interface_prioritizer_add_interface(nr_interface_prioritizer *ifp,
+ nr_local_addr *addr)
+ {
+ return ifp->vtbl->add_interface(ifp->obj, addr);
+ }
+
+int nr_interface_prioritizer_get_priority(nr_interface_prioritizer *ifp,
+ const char *key, UCHAR *interface_preference)
+ {
+ return ifp->vtbl->get_priority(ifp->obj,key,interface_preference);
+ }
+
+int nr_interface_prioritizer_sort_preference(nr_interface_prioritizer *ifp)
+ {
+ return ifp->vtbl->sort_preference(ifp->obj);
+ }
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h
new file mode 100644
index 000000000..c8a36526c
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h
@@ -0,0 +1,66 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _nr_interface_prioritizer
+#define _nr_interface_prioritizer
+
+#include "transport_addr.h"
+#include "local_addr.h"
+
+typedef struct nr_interface_prioritizer_vtbl_ {
+ int (*add_interface)(void *obj, nr_local_addr *iface);
+ int (*get_priority)(void *obj, const char *key, UCHAR *pref);
+ int (*sort_preference)(void *obj);
+ int (*destroy)(void **obj);
+} nr_interface_prioritizer_vtbl;
+
+typedef struct nr_interface_prioritizer_ {
+ void *obj;
+ nr_interface_prioritizer_vtbl *vtbl;
+} nr_interface_prioritizer;
+
+int nr_interface_prioritizer_create_int(void *obj, nr_interface_prioritizer_vtbl *vtbl,
+ nr_interface_prioritizer **prioritizer);
+
+int nr_interface_prioritizer_destroy(nr_interface_prioritizer **prioritizer);
+
+int nr_interface_prioritizer_add_interface(nr_interface_prioritizer *prioritizer,
+ nr_local_addr *addr);
+
+int nr_interface_prioritizer_get_priority(nr_interface_prioritizer *prioritizer,
+ const char *key, UCHAR *interface_preference);
+
+int nr_interface_prioritizer_sort_preference(nr_interface_prioritizer *prioritizer);
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
new file mode 100644
index 000000000..b26f2d278
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
@@ -0,0 +1,686 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <nr_api.h>
+
+#include <assert.h>
+
+#include "nr_proxy_tunnel.h"
+
+#define MAX_HTTP_CONNECT_ADDR_SIZE 256
+#define MAX_HTTP_CONNECT_BUFFER_SIZE 1024
+#define MAX_ALPN_LENGTH 64
+#ifndef CRLF
+#define CRLF "\r\n"
+#endif
+#define END_HEADERS CRLF CRLF
+
+typedef enum {
+ PROXY_TUNNEL_NONE=0,
+ PROXY_TUNNEL_REQUESTED,
+ PROXY_TUNNEL_CONNECTED,
+ PROXY_TUNNEL_CLOSED,
+ PROXY_TUNNEL_FAILED
+} nr_socket_proxy_tunnel_state;
+
+typedef struct nr_socket_proxy_tunnel_ {
+ nr_proxy_tunnel_config *config;
+ nr_socket *inner;
+ nr_transport_addr remote_addr;
+ nr_socket_proxy_tunnel_state state;
+ char buffer[MAX_HTTP_CONNECT_BUFFER_SIZE];
+ size_t buffered_bytes;
+ void *resolver_handle;
+} nr_socket_proxy_tunnel;
+
+typedef struct nr_socket_wrapper_factory_proxy_tunnel_ {
+ nr_proxy_tunnel_config *config;
+} nr_socket_wrapper_factory_proxy_tunnel;
+
+static int nr_socket_proxy_tunnel_destroy(void **objpp);
+static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_proxy_tunnel_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len, size_t *written);
+static int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen, size_t *len);
+static int nr_socket_proxy_tunnel_close(void *obj);
+
+int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp);
+
+int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj,
+ nr_socket *inner,
+ nr_socket **socketpp);
+
+int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp);
+
+static nr_socket_vtbl nr_socket_proxy_tunnel_vtbl={
+ 1,
+ nr_socket_proxy_tunnel_destroy,
+ 0,
+ 0,
+ nr_socket_proxy_tunnel_getfd,
+ nr_socket_proxy_tunnel_getaddr,
+ nr_socket_proxy_tunnel_connect,
+ nr_socket_proxy_tunnel_write,
+ nr_socket_proxy_tunnel_read,
+ nr_socket_proxy_tunnel_close
+};
+
+static int send_http_connect(nr_socket_proxy_tunnel *sock)
+{
+ int r, _status;
+ int port;
+ int printed;
+ char addr[MAX_HTTP_CONNECT_ADDR_SIZE];
+ char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + MAX_ALPN_LENGTH + 128];
+ size_t offset = 0;
+ size_t bytes_sent;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect");
+
+ if ((r=nr_transport_addr_get_port(&sock->remote_addr, &port))) {
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_get_addrstring(&sock->remote_addr, addr, sizeof(addr)))) {
+ ABORT(r);
+ }
+
+ printed = snprintf(mesg + offset, sizeof(mesg) - offset,
+ "CONNECT %s:%d HTTP/1.0", addr, port);
+ offset += printed;
+ if (printed < 0 || (offset >= sizeof(mesg))) {
+ ABORT(R_FAILED);
+ }
+
+ if (sock->config->alpn) {
+ printed = snprintf(mesg + offset, sizeof(mesg) - offset,
+ CRLF "ALPN: %s", sock->config->alpn);
+ offset += printed;
+ if (printed < 0 || (offset >= sizeof(mesg))) {
+ ABORT(R_FAILED);
+ }
+ }
+ if (offset + sizeof(END_HEADERS) >= sizeof(mesg)) {
+ ABORT(R_FAILED);
+ }
+ memcpy(mesg + offset, END_HEADERS, strlen(END_HEADERS));
+ offset += strlen(END_HEADERS);
+
+ if ((r=nr_socket_write(sock->inner, mesg, offset, &bytes_sent, 0))) {
+ ABORT(r);
+ }
+
+ if (bytes_sent < offset) {
+ /* TODO(bug 1116583): buffering and wait for */
+ r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect should be buffering %lu", (unsigned long)bytes_sent);
+ ABORT(R_IO_ERROR);
+ }
+
+ sock->state = PROXY_TUNNEL_REQUESTED;
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+static char *find_http_terminator(char *response, size_t len)
+{
+ char *term = response;
+ char *end = response + len;
+ int N = strlen(END_HEADERS);
+
+ for (; term = memchr(term, '\r', end - term); ++term) {
+ if (end - term >= N && memcmp(term, END_HEADERS, N) == 0) {
+ return term;
+ }
+ }
+
+ return NULL;
+}
+
+static int parse_http_response(char *begin, char *end, unsigned int *status)
+{
+ size_t len = end - begin;
+ char response[MAX_HTTP_CONNECT_BUFFER_SIZE + 1];
+
+ // len should *never* be greater than nr_socket_proxy_tunnel::buffered_bytes.
+ // Which in turn should never be greater nr_socket_proxy_tunnel::buffer size.
+ assert(len <= MAX_HTTP_CONNECT_BUFFER_SIZE);
+ if (len > MAX_HTTP_CONNECT_BUFFER_SIZE) {
+ return R_BAD_DATA;
+ }
+
+ memcpy(response, begin, len);
+ response[len] = '\0';
+
+ // http://www.rfc-editor.org/rfc/rfc7230.txt
+ // status-line = HTTP-version SP status-code SP reason-phrase CRLF
+ // HTTP-version = HTTP-name "/" DIGIT "." DIGIT
+ // HTTP-name = "HTTP" ; "HTTP", case-sensitive
+ // status-code = 3DIGIT
+
+ if (sscanf(response, "HTTP/%*u.%*u %u", status) != 1) {
+ r_log(LOG_GENERIC,LOG_WARNING,"parse_http_response failed to find status (%s)", response);
+ return R_BAD_DATA;
+ }
+
+ return 0;
+}
+
+static int nr_socket_proxy_tunnel_destroy(void **objpp)
+{
+ nr_socket_proxy_tunnel *sock;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_destroy");
+
+ if (!objpp || !*objpp)
+ return 0;
+
+ sock = (nr_socket_proxy_tunnel *)*objpp;
+ *objpp = 0;
+
+ if (sock->resolver_handle) {
+ nr_resolver_cancel(sock->config->resolver, sock->resolver_handle);
+ }
+
+ nr_proxy_tunnel_config_destroy(&sock->config);
+ nr_socket_destroy(&sock->inner);
+ RFREE(sock);
+
+ return 0;
+}
+
+static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd)
+{
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getfd");
+
+ return nr_socket_getfd(sock->inner, fd);
+}
+
+static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp)
+{
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getaddr");
+
+ return nr_socket_getaddr(sock->inner, addrp);
+}
+
+static int nr_socket_proxy_tunnel_resolved_cb(void *obj, nr_transport_addr *proxy_addr)
+{
+ int r, _status;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_resolved_cb");
+
+ /* Mark the socket resolver as completed */
+ sock->resolver_handle = 0;
+
+ if (proxy_addr) {
+ r_log(LOG_GENERIC,LOG_DEBUG,"Resolved proxy address %s -> %s",
+ sock->config->proxy_host, proxy_addr->as_string);
+ }
+ else {
+ r_log(LOG_GENERIC,LOG_WARNING,"Failed to resolve proxy %s",
+ sock->config->proxy_host);
+ /* TODO: Mozilla bug 1241758: because of the callback the return value goes
+ * nowhere, so we can't mark the candidate as failed, so everything depends
+ * on the overall timeouts in this case. */
+ sock->state = PROXY_TUNNEL_FAILED;
+ ABORT(R_NOT_FOUND);
+ }
+
+ if ((r=nr_socket_connect(sock->inner, proxy_addr))) {
+ ABORT(r);
+ }
+
+ _status = 0;
+abort:
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_connect(void *obj, nr_transport_addr *addr)
+{
+ int r, _status;
+ int has_addr;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+ nr_proxy_tunnel_config *config = sock->config;
+ nr_transport_addr proxy_addr, local_addr;
+ nr_resolver_resource resource;
+
+ if ((r=nr_transport_addr_copy(&sock->remote_addr, addr))) {
+ ABORT(r);
+ }
+
+ assert(config->proxy_host);
+
+ /* Check if the proxy_host is already an IP address */
+ has_addr = !nr_str_port_to_transport_addr(config->proxy_host,
+ config->proxy_port, IPPROTO_TCP, &proxy_addr);
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: %s", config->proxy_host);
+
+ if (!has_addr && !config->resolver) {
+ r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect name resolver not configured");
+ ABORT(R_NOT_FOUND);
+ }
+
+ if (!has_addr) {
+ resource.domain_name=config->proxy_host;
+ resource.port=config->proxy_port;
+ resource.stun_turn=NR_RESOLVE_PROTOCOL_TURN;
+ resource.transport_protocol=IPPROTO_TCP;
+
+ if ((r=nr_socket_getaddr(sock->inner, &local_addr))) {
+ r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect failed to get local address");
+ ABORT(r);
+ }
+
+ switch(local_addr.ip_version) {
+ case NR_IPV4:
+ resource.address_family=AF_INET;
+ break;
+ case NR_IPV6:
+ resource.address_family=AF_INET6;
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: nr_resolver_resolve");
+ if ((r=nr_resolver_resolve(config->resolver, &resource,
+ nr_socket_proxy_tunnel_resolved_cb, (void *)sock, &sock->resolver_handle))) {
+ r_log(LOG_GENERIC,LOG_ERR,"Could not invoke DNS resolver");
+ ABORT(r);
+ }
+
+ ABORT(R_WOULDBLOCK);
+ }
+
+ if ((r=nr_socket_connect(sock->inner, &proxy_addr))) {
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len,
+ size_t *written)
+{
+ int r, _status;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_write");
+
+ if (sock->state >= PROXY_TUNNEL_CLOSED) {
+ return R_FAILED;
+ }
+
+ if (sock->state == PROXY_TUNNEL_NONE) {
+ if ((r=send_http_connect(sock))) {
+ ABORT(r);
+ }
+ }
+
+ if (sock->state != PROXY_TUNNEL_CONNECTED) {
+ return R_WOULDBLOCK;
+ }
+
+ if ((r=nr_socket_write(sock->inner, msg, len, written, 0))) {
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen,
+ size_t *len)
+{
+ int r, _status;
+ char *ptr, *http_term;
+ size_t bytes_read, available_buffer_len, maxlen_int;
+ size_t pending;
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+ unsigned int http_status;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_read");
+
+ *len = 0;
+
+ if (sock->state >= PROXY_TUNNEL_CLOSED) {
+ return R_FAILED;
+ }
+
+ if (sock->state == PROXY_TUNNEL_CONNECTED) {
+ return nr_socket_read(sock->inner, buf, maxlen, len, 0);
+ }
+
+ if (sock->buffered_bytes >= sizeof(sock->buffer)) {
+ r_log(LOG_GENERIC,LOG_ERR,"buffer filled waiting for CONNECT response");
+ assert(sock->buffered_bytes == sizeof(sock->buffer));
+ ABORT(R_INTERNAL);
+ }
+
+ /* Do not read more than maxlen bytes */
+ available_buffer_len = sizeof(sock->buffer) - sock->buffered_bytes;
+ maxlen_int = maxlen < available_buffer_len ? maxlen : available_buffer_len;
+ if ((r=nr_socket_read(sock->inner, sock->buffer + sock->buffered_bytes,
+ maxlen_int, &bytes_read, 0))) {
+ ABORT(r);
+ }
+
+ sock->buffered_bytes += bytes_read;
+
+ if (http_term = find_http_terminator(sock->buffer, sock->buffered_bytes)) {
+ if ((r = parse_http_response(sock->buffer, http_term, &http_status))) {
+ ABORT(r);
+ }
+
+ /* TODO (bug 1115934): Handle authentication challenges. */
+ if (http_status < 200 || http_status >= 300) {
+ r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_read unable to connect %u",
+ http_status);
+ ABORT(R_FAILED);
+ }
+
+ sock->state = PROXY_TUNNEL_CONNECTED;
+
+ ptr = http_term + strlen(END_HEADERS);
+ pending = sock->buffered_bytes - (ptr - sock->buffer);
+
+ if (pending == 0) {
+ ABORT(R_WOULDBLOCK);
+ }
+
+ assert(pending <= maxlen);
+ *len = pending;
+
+ memcpy(buf, ptr, *len);
+ }
+
+ _status=0;
+abort:
+ if (_status && _status != R_WOULDBLOCK) {
+ sock->state = PROXY_TUNNEL_FAILED;
+ }
+ return(_status);
+}
+
+int nr_socket_proxy_tunnel_close(void *obj)
+{
+ nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_close");
+
+ if (sock->resolver_handle) {
+ nr_resolver_cancel(sock->config->resolver, sock->resolver_handle);
+ sock->resolver_handle = 0;
+ }
+
+ sock->state = PROXY_TUNNEL_CLOSED;
+
+ return nr_socket_close(sock->inner);
+}
+
+int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **configpp)
+{
+ int _status;
+ nr_proxy_tunnel_config *configp=0;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_create");
+
+ if (!(configp=RCALLOC(sizeof(nr_proxy_tunnel_config))))
+ ABORT(R_NO_MEMORY);
+
+ *configpp=configp;
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **configpp)
+{
+ nr_proxy_tunnel_config *configp;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_destroy");
+
+ if (!configpp || !*configpp)
+ return 0;
+
+ configp = *configpp;
+ *configpp = 0;
+
+ RFREE(configp->proxy_host);
+ RFREE(configp->alpn);
+ RFREE(configp);
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config,
+ const char *host, UINT2 port)
+{
+ char *hostdup;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_proxy %s %d", host, port);
+
+ if (!host) {
+ return R_BAD_ARGS;
+ }
+
+ if (!(hostdup = r_strdup(host))) {
+ return R_NO_MEMORY;
+ }
+
+ if (config->proxy_host) {
+ RFREE(config->proxy_host);
+ }
+
+ config->proxy_host = hostdup;
+ config->proxy_port = port;
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config,
+ nr_resolver *resolver)
+{
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_resolver");
+
+ config->resolver = resolver;
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config,
+ const char *alpn)
+{
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_alpn");
+
+ if (alpn && (strlen(alpn) > MAX_ALPN_LENGTH)) {
+ return R_BAD_ARGS;
+ }
+
+ if (config->alpn) {
+ RFREE(config->alpn);
+ }
+
+ config->alpn = NULL;
+
+ if (alpn) {
+ char *alpndup = r_strdup(alpn);
+
+ if (!alpndup) {
+ return R_NO_MEMORY;
+ }
+
+ config->alpn = alpndup;
+ }
+
+ return 0;
+}
+
+int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp)
+{
+ int r,_status;
+ nr_proxy_tunnel_config *copy = 0;
+
+ if ((r=nr_proxy_tunnel_config_create(&copy)))
+ ABORT(r);
+
+ if ((r=nr_proxy_tunnel_config_set_proxy(copy, config->proxy_host, config->proxy_port)))
+ ABORT(r);
+
+ if ((r=nr_proxy_tunnel_config_set_resolver(copy, config->resolver)))
+ ABORT(r);
+
+ if ((r=nr_proxy_tunnel_config_set_alpn(copy, config->alpn)))
+ ABORT(r);
+
+ *copypp = copy;
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_proxy_tunnel_config_destroy(&copy);
+ }
+ return(_status);
+}
+
+
+int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config,
+ nr_socket *inner,
+ nr_socket **socketpp)
+{
+ int r, _status;
+ nr_socket_proxy_tunnel *sock=0;
+ void *sockv;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_create");
+
+ if (!config) {
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (!(sock=RCALLOC(sizeof(nr_socket_proxy_tunnel)))) {
+ ABORT(R_NO_MEMORY);
+ }
+
+ sock->inner = inner;
+
+ if ((r=nr_proxy_tunnel_config_copy(config, &sock->config)))
+ ABORT(r);
+
+ if ((r=nr_socket_create_int(sock, &nr_socket_proxy_tunnel_vtbl, socketpp))) {
+ ABORT(r);
+ }
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_created");
+
+ _status=0;
+abort:
+ if (_status) {
+ sockv = sock;
+ nr_socket_proxy_tunnel_destroy(&sockv);
+ }
+ return(_status);
+}
+
+int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj,
+ nr_socket *inner,
+ nr_socket **socketpp)
+{
+ nr_socket_wrapper_factory_proxy_tunnel *wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)obj;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_wrap");
+
+ return nr_socket_proxy_tunnel_create(wrapper->config, inner, socketpp);
+}
+
+
+int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp) {
+ nr_socket_wrapper_factory_proxy_tunnel *wrapper;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_destroy");
+
+ if (!objpp || !*objpp)
+ return 0;
+
+ wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)*objpp;
+ *objpp = 0;
+
+ nr_proxy_tunnel_config_destroy(&wrapper->config);
+ RFREE(wrapper);
+
+ return 0;
+}
+
+static nr_socket_wrapper_factory_vtbl proxy_tunnel_wrapper_vtbl = {
+ nr_socket_wrapper_factory_proxy_tunnel_wrap,
+ nr_socket_wrapper_factory_proxy_tunnel_destroy
+};
+
+int nr_socket_wrapper_factory_proxy_tunnel_create(nr_proxy_tunnel_config *config,
+ nr_socket_wrapper_factory **factory) {
+ int r,_status;
+ nr_socket_wrapper_factory_proxy_tunnel *wrapper=0;
+ void *wrapperv;
+
+ r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_create");
+
+ if (!(wrapper=RCALLOC(sizeof(nr_socket_wrapper_factory_proxy_tunnel))))
+ ABORT(R_NO_MEMORY);
+
+ if ((r=nr_proxy_tunnel_config_copy(config, &wrapper->config)))
+ ABORT(r);
+
+ if ((r=nr_socket_wrapper_factory_create_int(wrapper, &proxy_tunnel_wrapper_vtbl, factory)))
+ ABORT(r);
+
+ _status=0;
+abort:
+ if (_status) {
+ wrapperv = wrapper;
+ nr_socket_wrapper_factory_proxy_tunnel_destroy(&wrapperv);
+ }
+ return(_status);
+}
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h
new file mode 100644
index 000000000..e9c4393e1
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h
@@ -0,0 +1,70 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _nr_proxy_tunnel_h
+#define _nr_proxy_tunnel_h
+
+#include "nr_socket.h"
+#include "nr_resolver.h"
+#include "nr_socket_wrapper.h"
+
+typedef struct nr_proxy_tunnel_config_ {
+ nr_resolver *resolver;
+ char *proxy_host;
+ UINT2 proxy_port;
+ char *alpn;
+} nr_proxy_tunnel_config;
+
+int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **config);
+
+int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **config);
+
+int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config,
+ const char* host, UINT2 port);
+
+int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config,
+ nr_resolver *resolver);
+
+int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config,
+ const char *alpn);
+
+int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config,
+ nr_socket *inner,
+ nr_socket **socketpp);
+
+int nr_socket_wrapper_factory_proxy_tunnel_create(nr_proxy_tunnel_config *config,
+ nr_socket_wrapper_factory **factory);
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_resolver.c b/media/mtransport/third_party/nICEr/src/net/nr_resolver.c
new file mode 100644
index 000000000..4dbf1bbe9
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_resolver.c
@@ -0,0 +1,85 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <nr_api.h>
+#include "nr_resolver.h"
+
+int nr_resolver_create_int(void *obj, nr_resolver_vtbl *vtbl, nr_resolver **resolverp)
+{
+ int _status;
+ nr_resolver *resolver=0;
+
+ if (!(resolver=RCALLOC(sizeof(nr_resolver))))
+ ABORT(R_NO_MEMORY);
+
+ resolver->obj=obj;
+ resolver->vtbl=vtbl;
+
+ *resolverp=resolver;
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_resolver_destroy(nr_resolver **resolverp)
+{
+ nr_resolver *resolver;
+
+ if(!resolverp || !*resolverp)
+ return(0);
+
+ resolver=*resolverp;
+ *resolverp=0;
+
+ resolver->vtbl->destroy(&resolver->obj);
+
+ RFREE(resolver);
+
+ return(0);
+}
+
+int nr_resolver_resolve(nr_resolver *resolver,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle)
+{
+ return resolver->vtbl->resolve(resolver->obj, resource, cb, cb_arg, handle);
+}
+
+int nr_resolver_cancel(nr_resolver *resolver, void *handle)
+{
+ return resolver->vtbl->cancel(resolver->obj, handle);
+}
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_resolver.h b/media/mtransport/third_party/nICEr/src/net/nr_resolver.h
new file mode 100644
index 000000000..376ba9998
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_resolver.h
@@ -0,0 +1,96 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _nr_resolver_h
+#define _nr_resolver_h
+
+#include "transport_addr.h"
+
+#define NR_RESOLVE_PROTOCOL_STUN 1
+#define NR_RESOLVE_PROTOCOL_TURN 2
+
+typedef struct nr_resolver_resource_ {
+ const char *domain_name;
+ UINT2 port;
+ int stun_turn;
+ UCHAR transport_protocol;
+ UCHAR address_family;
+} nr_resolver_resource;
+
+typedef struct nr_resolver_vtbl_ {
+ int (*destroy)(void **obj);
+ int (*resolve)(void *obj,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle);
+ int (*cancel)(void *obj, void *handle);
+} nr_resolver_vtbl;
+
+typedef struct nr_resolver_ {
+ void *obj;
+ nr_resolver_vtbl *vtbl;
+} nr_resolver;
+
+
+/*
+ The convention here is that the provider of this interface
+ must generate a void *obj, and a vtbl and then call
+ nr_resolver_create_int() to allocate the generic wrapper
+ object.
+
+ The vtbl must contain implementations for all the functions
+ listed.
+
+ The nr_resolver_destroy() function (and hence vtbl->destroy)
+ will be called when the consumer of the resolver is done
+ with it. That is the signal that it is safe to clean up
+ the resources associated with obj. No other function will
+ be called afterwards.
+*/
+int nr_resolver_create_int(void *obj, nr_resolver_vtbl *vtbl,
+ nr_resolver **resolverp);
+int nr_resolver_destroy(nr_resolver **resolverp);
+
+/* Request resolution of a domain */
+int nr_resolver_resolve(nr_resolver *resolver,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle);
+
+/* Cancel a requested resolution. No callback will fire. */
+int nr_resolver_cancel(nr_resolver *resolver, void *handle);
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket.c b/media/mtransport/third_party/nICEr/src/net/nr_socket.c
new file mode 100644
index 000000000..d12f26454
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.c
@@ -0,0 +1,191 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: nr_socket.c,v 1.2 2008/04/28 17:59:02 ekr Exp $";
+
+#include <assert.h>
+#include <nr_api.h>
+#include "nr_socket.h"
+#include "local_addr.h"
+
+#define CHECK_DEFINED(f) assert(sock->vtbl->f); if (!sock->vtbl->f) ERETURN(R_INTERNAL);
+int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp)
+ {
+ int _status;
+ nr_socket *sock=0;
+
+ if(!(sock=RCALLOC(sizeof(nr_socket))))
+ ABORT(R_NO_MEMORY);
+
+ assert(vtbl->version >= 1 && vtbl->version <= 2);
+ if (vtbl->version < 1 || vtbl->version > 2)
+ ABORT(R_INTERNAL);
+
+ sock->obj=obj;
+ sock->vtbl=vtbl;
+
+ *sockp=sock;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_socket_destroy(nr_socket **sockp)
+ {
+ nr_socket *sock;
+
+ if(!sockp || !*sockp)
+ return(0);
+
+
+ sock=*sockp;
+ *sockp=0;
+
+ CHECK_DEFINED(destroy);
+
+ assert(sock->vtbl);
+ if (sock->vtbl)
+ sock->vtbl->destroy(&sock->obj);
+
+ RFREE(sock);
+
+ return(0);
+ }
+
+int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len, int flags,
+ nr_transport_addr *addr)
+ {
+ CHECK_DEFINED(ssendto);
+ return sock->vtbl->ssendto(sock->obj,msg,len,flags,addr);
+ }
+
+int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
+ size_t *len, int flags, nr_transport_addr *addr)
+ {
+ CHECK_DEFINED(srecvfrom);
+ return sock->vtbl->srecvfrom(sock->obj, buf, maxlen, len, flags, addr);
+ }
+
+int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd)
+ {
+ CHECK_DEFINED(getfd);
+ return sock->vtbl->getfd(sock->obj, fd);
+ }
+
+int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp)
+ {
+ CHECK_DEFINED(getaddr);
+ return sock->vtbl->getaddr(sock->obj, addrp);
+ }
+
+int nr_socket_close(nr_socket *sock)
+ {
+ CHECK_DEFINED(close);
+ return sock->vtbl->close(sock->obj);
+ }
+
+int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr)
+ {
+ CHECK_DEFINED(connect);
+ return sock->vtbl->connect(sock->obj, addr);
+ }
+
+int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags)
+ {
+ CHECK_DEFINED(swrite);
+ return sock->vtbl->swrite(sock->obj, msg, len, written);
+ }
+
+
+int nr_socket_read(nr_socket *sock,void * restrict buf, size_t maxlen,
+ size_t *len, int flags)
+ {
+ CHECK_DEFINED(sread);
+ return sock->vtbl->sread(sock->obj, buf, maxlen, len);
+ }
+
+int nr_socket_listen(nr_socket *sock, int backlog)
+ {
+ assert(sock->vtbl->version >=2 );
+ CHECK_DEFINED(listen);
+ return sock->vtbl->listen(sock->obj, backlog);
+ }
+
+int nr_socket_accept(nr_socket *sock, nr_transport_addr *addrp, nr_socket **sockp)
+{
+ assert(sock->vtbl->version >= 2);
+ CHECK_DEFINED(accept);
+ return sock->vtbl->accept(sock->obj, addrp, sockp);
+}
+
+
+int nr_socket_factory_create_int(void *obj,
+ nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp)
+ {
+ int _status;
+ nr_socket_factory *factoryp=0;
+
+ if(!(factoryp=RCALLOC(sizeof(nr_socket_factory))))
+ ABORT(R_NO_MEMORY);
+
+ factoryp->obj = obj;
+ factoryp->vtbl = vtbl;
+
+ *factorypp = factoryp;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_socket_factory_destroy(nr_socket_factory **factorypp)
+ {
+ nr_socket_factory *factoryp;
+
+ if (!factorypp || !*factorypp)
+ return (0);
+
+ factoryp = *factorypp;
+ *factorypp = NULL;
+ factoryp->vtbl->destroy(&factoryp->obj);
+ RFREE(factoryp);
+ return (0);
+ }
+
+int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp)
+ {
+ return factory->vtbl->create_socket(factory->obj, addr, sockp);
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket.h b/media/mtransport/third_party/nICEr/src/net/nr_socket.h
new file mode 100644
index 000000000..95f62e413
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.h
@@ -0,0 +1,121 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _nr_socket_h
+#define _nr_socket_h
+
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif
+
+#include "transport_addr.h"
+#include "csi_platform.h"
+
+#ifdef __cplusplus
+#define restrict
+#elif defined(WIN32)
+#define restrict __restrict
+#endif
+
+typedef enum {
+ TCP_TYPE_NONE=0,
+ TCP_TYPE_ACTIVE,
+ TCP_TYPE_PASSIVE,
+ TCP_TYPE_SO,
+ TCP_TYPE_MAX
+} nr_socket_tcp_type;
+
+typedef struct nr_socket_ nr_socket;
+
+typedef struct nr_socket_vtbl_ {
+ UINT4 version; /* Currently 2 */
+ int (*destroy)(void **obj);
+ int (*ssendto)(void *obj,const void *msg, size_t len, int flags,
+ nr_transport_addr *addr);
+ int (*srecvfrom)(void *obj,void * restrict buf, size_t maxlen, size_t *len, int flags,
+ nr_transport_addr *addr);
+ int (*getfd)(void *obj, NR_SOCKET *fd);
+ int (*getaddr)(void *obj, nr_transport_addr *addrp);
+ int (*connect)(void *obj, nr_transport_addr *addr);
+ int (*swrite)(void *obj,const void *msg, size_t len, size_t *written);
+ int (*sread)(void *obj,void * restrict buf, size_t maxlen, size_t *len);
+ int (*close)(void *obj);
+
+ /* available since version 2 */
+ int (*listen)(void *obj, int backlog);
+ int (*accept)(void *obj, nr_transport_addr *addrp, nr_socket **sockp);
+} nr_socket_vtbl;
+
+
+struct nr_socket_ {
+ void *obj;
+ nr_socket_vtbl *vtbl;
+};
+
+typedef struct nr_socket_factory_vtbl_ {
+ int (*create_socket)(void *obj, nr_transport_addr *addr, nr_socket **sockp);
+ int (*destroy)(void **obj);
+} nr_socket_factory_vtbl;
+
+typedef struct nr_socket_factory_ {
+ void *obj;
+ nr_socket_factory_vtbl *vtbl;
+} nr_socket_factory;
+
+/* To be called by constructors */
+int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp);
+int nr_socket_destroy(nr_socket **sockp);
+int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len,
+ int flags,nr_transport_addr *addr);
+int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen,
+ size_t *len, int flags, nr_transport_addr *addr);
+int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd);
+int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp);
+int nr_socket_close(nr_socket *sock);
+int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr);
+int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags);
+int nr_socket_read(nr_socket *sock, void * restrict buf, size_t maxlen, size_t *len, int flags);
+int nr_socket_listen(nr_socket *sock, int backlog);
+int nr_socket_accept(nr_socket *sock, nr_transport_addr *addrp, nr_socket **sockp);
+
+int nr_socket_factory_create_int(void *obj, nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp);
+int nr_socket_factory_destroy(nr_socket_factory **factoryp);
+int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_local.h b/media/mtransport/third_party/nICEr/src/net/nr_socket_local.h
new file mode 100644
index 000000000..a2f813ff6
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_local.h
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _nr_socket_local_h
+#define _nr_socket_local_h
+
+int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c
new file mode 100644
index 000000000..12bbea795
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c
@@ -0,0 +1,620 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2014, Mozilla
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <assert.h>
+#include <sys/types.h>
+
+#include "nr_api.h"
+#include "ice_ctx.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "nr_socket_multi_tcp.h"
+#include "nr_socket_buffered_stun.h"
+#include "async_timer.h"
+
+typedef struct nr_tcp_socket_ctx_ {
+ nr_socket * inner;
+ nr_transport_addr remote_addr;
+ int is_framed;
+
+ TAILQ_ENTRY(nr_tcp_socket_ctx_) entry;
+} nr_tcp_socket_ctx;
+
+typedef TAILQ_HEAD(nr_tcp_socket_head_,nr_tcp_socket_ctx_) nr_tcp_socket_head;
+
+static void nr_tcp_socket_readable_cb(NR_SOCKET s, int how, void *arg);
+
+static int nr_tcp_socket_ctx_destroy(nr_tcp_socket_ctx **objp)
+ {
+ nr_tcp_socket_ctx *sock;
+
+ if (!objp || !*objp)
+ return(0);
+
+ sock=*objp;
+ *objp=0;
+
+ nr_socket_destroy(&sock->inner);
+
+ RFREE(sock);
+
+ return(0);
+ }
+
+/* This takes ownership of nrsock whether it fails or not. */
+static int nr_tcp_socket_ctx_create(nr_socket *nrsock, int is_framed,
+ int max_pending, nr_tcp_socket_ctx **sockp)
+ {
+ int r, _status;
+ nr_tcp_socket_ctx *sock = 0;
+ nr_socket *tcpsock;
+
+ if (!(sock = RCALLOC(sizeof(nr_tcp_socket_ctx)))) {
+ nr_socket_destroy(&nrsock);
+ ABORT(R_NO_MEMORY);
+ }
+
+ if ((r=nr_socket_buffered_stun_create(nrsock, max_pending, is_framed ? ICE_TCP_FRAMING : TURN_TCP_FRAMING, &tcpsock))){
+ nr_socket_destroy(&nrsock);
+ ABORT(r);
+ }
+
+ sock->inner=tcpsock;
+ sock->is_framed=is_framed;
+
+ if ((r=nr_ip4_port_to_transport_addr(ntohl(INADDR_ANY), 0, IPPROTO_TCP, &sock->remote_addr)))
+ ABORT(r);
+
+ *sockp=sock;
+
+ _status=0;
+abort:
+ if (_status) {
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d",__FILE__,__LINE__,__FUNCTION__,_status);
+ nr_tcp_socket_ctx_destroy(&sock);
+ }
+ return(_status);
+ }
+
+static int nr_tcp_socket_ctx_initialize(nr_tcp_socket_ctx *tcpsock,
+ nr_transport_addr *addr, void* cb_arg)
+ {
+ int r, _status;
+ NR_SOCKET fd;
+
+ if ((r=nr_transport_addr_copy(&tcpsock->remote_addr, addr)))
+ ABORT(r);
+ if ((r=nr_socket_getfd(tcpsock->inner, &fd)))
+ ABORT(r);
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, nr_tcp_socket_readable_cb, cb_arg);
+
+ _status=0;
+ abort:
+ if (_status)
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status);
+ return(_status);
+ }
+
+typedef struct nr_socket_multi_tcp_ {
+ nr_ice_ctx *ctx;
+ nr_socket *listen_socket;
+ nr_tcp_socket_head sockets;
+ nr_socket_tcp_type tcp_type;
+ nr_transport_addr addr;
+ NR_async_cb readable_cb;
+ void *readable_cb_arg;
+ int max_pending;
+} nr_socket_multi_tcp;
+
+static int nr_socket_multi_tcp_destroy(void **objp);
+static int nr_socket_multi_tcp_sendto(void *obj,const void *msg, size_t len,
+ int flags, nr_transport_addr *to);
+static int nr_socket_multi_tcp_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
+static int nr_socket_multi_tcp_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_multi_tcp_close(void *obj);
+static int nr_socket_multi_tcp_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_multi_tcp_listen(void *obj, int backlog);
+
+static nr_socket_vtbl nr_socket_multi_tcp_vtbl={
+ 2,
+ nr_socket_multi_tcp_destroy,
+ nr_socket_multi_tcp_sendto,
+ nr_socket_multi_tcp_recvfrom,
+ 0,
+ nr_socket_multi_tcp_getaddr,
+ nr_socket_multi_tcp_connect,
+ 0,
+ 0,
+ nr_socket_multi_tcp_close,
+ nr_socket_multi_tcp_listen,
+ 0
+};
+
+static int nr_socket_multi_tcp_create_stun_server_socket(
+ nr_socket_multi_tcp *sock, nr_ice_stun_server * stun_server,
+ nr_transport_addr *addr, int max_pending)
+ {
+ int r, _status;
+ nr_tcp_socket_ctx *tcp_socket_ctx=0;
+ nr_socket * nrsock;
+
+ if (stun_server->transport!=IPPROTO_TCP) {
+ r_log(LOG_ICE,LOG_INFO,"%s:%d function %s skipping UDP STUN server(addr:%s)",__FILE__,__LINE__,__FUNCTION__,stun_server->u.addr.as_string);
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR &&
+ nr_transport_addr_cmp(&stun_server->u.addr,addr,NR_TRANSPORT_ADDR_CMP_MODE_VERSION)) {
+ r_log(LOG_ICE,LOG_INFO,"%s:%d function %s skipping STUN with different IP version (%u) than local socket (%u),",__FILE__,__LINE__,__FUNCTION__,stun_server->u.addr.ip_version,addr->ip_version);
+ ABORT(R_BAD_ARGS);
+ }
+
+ if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory,addr, &nrsock)))
+ ABORT(r);
+
+ /* This takes ownership of nrsock whether it fails or not. */
+ if ((r=nr_tcp_socket_ctx_create(nrsock, 0, max_pending, &tcp_socket_ctx)))
+ ABORT(r);
+
+ if (stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) {
+ nr_transport_addr stun_server_addr;
+
+ nr_transport_addr_copy(&stun_server_addr, &stun_server->u.addr);
+ r=nr_socket_connect(tcp_socket_ctx->inner, &stun_server_addr);
+ if (r && r!=R_WOULDBLOCK) {
+ r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s connect to STUN server(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,stun_server_addr.as_string,r);
+ ABORT(r);
+ }
+
+ if ((r=nr_tcp_socket_ctx_initialize(tcp_socket_ctx, &stun_server_addr, sock)))
+ ABORT(r);
+ }
+
+ TAILQ_INSERT_TAIL(&sock->sockets, tcp_socket_ctx, entry);
+
+ _status=0;
+ abort:
+ if (_status) {
+ nr_tcp_socket_ctx_destroy(&tcp_socket_ctx);
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status);
+ }
+ return(_status);
+ }
+
+int nr_socket_multi_tcp_create(struct nr_ice_ctx_ *ctx,
+ nr_transport_addr *addr, nr_socket_tcp_type tcp_type,
+ int precreated_so_count, int max_pending, nr_socket **sockp)
+ {
+ int i=0;
+ int r, _status;
+ nr_socket_multi_tcp *sock=0;
+ nr_tcp_socket_ctx *tcp_socket_ctx;
+ nr_socket * nrsock;
+
+ if (!(sock = RCALLOC(sizeof(nr_socket_multi_tcp))))
+ ABORT(R_NO_MEMORY);
+
+ TAILQ_INIT(&sock->sockets);
+
+ sock->ctx=ctx;
+ sock->max_pending=max_pending;
+ sock->tcp_type=tcp_type;
+ nr_transport_addr_copy(&sock->addr, addr);
+
+ if((tcp_type==TCP_TYPE_PASSIVE) &&
+ ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, addr, &sock->listen_socket))))
+ ABORT(r);
+
+ if (tcp_type!=TCP_TYPE_ACTIVE) {
+ if (sock->ctx && sock->ctx->stun_servers) {
+ for (i=0; i<sock->ctx->stun_server_ct; ++i) {
+ if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock,
+ sock->ctx->stun_servers+i, addr, max_pending))) {
+ if (r!=R_BAD_ARGS) {
+ r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed to connect STUN server from addr:%s with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,r);
+ }
+ }
+ }
+ }
+ if (sock->ctx && sock->ctx->turn_servers) {
+ for (i=0; i<sock->ctx->turn_server_ct; ++i) {
+ if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock,
+ &(sock->ctx->turn_servers[i]).turn_server, addr, max_pending))) {
+ if (r!=R_BAD_ARGS) {
+ r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed to connect TURN server from addr:%s with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,r);
+ }
+ }
+ }
+ }
+ }
+
+ if ((tcp_type==TCP_TYPE_SO)) {
+ for (i=0; i<precreated_so_count; ++i) {
+
+ if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, addr, &nrsock)))
+ ABORT(r);
+
+ /* This takes ownership of nrsock whether it fails or not. */
+ if ((r=nr_tcp_socket_ctx_create(nrsock, 1, max_pending, &tcp_socket_ctx))){
+ ABORT(r);
+ }
+ TAILQ_INSERT_TAIL(&sock->sockets, tcp_socket_ctx, entry);
+ }
+ }
+
+ if((r=nr_socket_create_int(sock, &nr_socket_multi_tcp_vtbl, sockp)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if (_status) {
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status);
+ nr_socket_multi_tcp_destroy((void**)&sock);
+ }
+ return(_status);
+ }
+
+int nr_socket_multi_tcp_set_readable_cb(nr_socket *sock,
+ NR_async_cb readable_cb, void *readable_cb_arg)
+ {
+ nr_socket_multi_tcp *mtcp_sock = (nr_socket_multi_tcp *)sock->obj;
+
+ mtcp_sock->readable_cb=readable_cb;
+ mtcp_sock->readable_cb_arg=readable_cb_arg;
+
+ return 0;
+ }
+
+#define PREALLOC_CONNECT_FRAMED 0
+#define PREALLOC_CONNECT_NON_FRAMED 1
+#define PREALLOC_DONT_CONNECT_UNLESS_SO 2
+
+static int nr_socket_multi_tcp_get_sock_connected_to(nr_socket_multi_tcp *sock,
+ nr_transport_addr *to, int preallocated_connect_mode, nr_socket **ret_sock)
+ {
+ int r, _status;
+ nr_tcp_socket_ctx *tcp_sock_ctx;
+ nr_socket * nrsock;
+
+ to->protocol=IPPROTO_TCP;
+
+ TAILQ_FOREACH(tcp_sock_ctx, &sock->sockets, entry) {
+ if (!nr_transport_addr_is_wildcard(&tcp_sock_ctx->remote_addr)) {
+ if (!nr_transport_addr_cmp(to, &tcp_sock_ctx->remote_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ *ret_sock=tcp_sock_ctx->inner;
+ return(0);
+ }
+ }
+ }
+
+ tcp_sock_ctx=NULL;
+ /* not connected yet */
+ if (sock->tcp_type != TCP_TYPE_ACTIVE) {
+ if (preallocated_connect_mode == PREALLOC_DONT_CONNECT_UNLESS_SO && sock->tcp_type != TCP_TYPE_SO)
+ ABORT(R_FAILED);
+
+ /* find free preallocated socket and connect */
+ TAILQ_FOREACH(tcp_sock_ctx, &sock->sockets, entry) {
+ if (nr_transport_addr_is_wildcard(&tcp_sock_ctx->remote_addr)) {
+ if (preallocated_connect_mode == PREALLOC_CONNECT_NON_FRAMED && tcp_sock_ctx->is_framed)
+ continue;
+ if (preallocated_connect_mode != PREALLOC_CONNECT_NON_FRAMED && !tcp_sock_ctx->is_framed)
+ continue;
+
+ if ((r=nr_socket_connect(tcp_sock_ctx->inner, to))){
+ if (r!=R_WOULDBLOCK)
+ ABORT(r);
+ }
+
+ if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, to, sock)))
+ ABORT(r);
+
+ *ret_sock=tcp_sock_ctx->inner;
+
+ return(0);
+ }
+ }
+ tcp_sock_ctx=NULL;
+ ABORT(R_FAILED);
+ }
+
+ /* if active type - create new socket for each new remote addr */
+ assert(sock->tcp_type == TCP_TYPE_ACTIVE);
+
+ if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, &sock->addr, &nrsock)))
+ ABORT(r);
+
+ /* This takes ownership of nrsock whether it fails or not. */
+ if ((r=nr_tcp_socket_ctx_create(nrsock, 1, sock->max_pending, &tcp_sock_ctx))){
+ ABORT(r);
+ }
+
+ TAILQ_INSERT_TAIL(&sock->sockets, tcp_sock_ctx, entry);
+
+ if ((r=nr_socket_connect(tcp_sock_ctx->inner, to))){
+ if (r!=R_WOULDBLOCK)
+ ABORT(r);
+ }
+
+ if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, to, sock)))
+ ABORT(r);
+
+ *ret_sock=tcp_sock_ctx->inner;
+ tcp_sock_ctx=NULL;
+
+ _status=0;
+ abort:
+ if (_status) {
+ if (tcp_sock_ctx) {
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d, tcp_sock_ctx remote_addr: %s",__FILE__,__LINE__,__FUNCTION__,_status, tcp_sock_ctx->remote_addr.as_string);
+ TAILQ_REMOVE(&sock->sockets, tcp_sock_ctx, entry);
+ nr_tcp_socket_ctx_destroy(&tcp_sock_ctx);
+ } else {
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d, tcp_sock_ctx=NULL",__FILE__,__LINE__,__FUNCTION__,_status);
+ }
+ }
+
+ return(_status);
+ }
+
+int nr_socket_multi_tcp_stun_server_connect(nr_socket *sock,
+ nr_transport_addr *addr)
+ {
+ int r, _status;
+ nr_socket_multi_tcp *mtcp_sock = (nr_socket_multi_tcp *)sock->obj;
+ nr_socket *nrsock;
+
+ assert(mtcp_sock->tcp_type != TCP_TYPE_ACTIVE);
+ if (mtcp_sock->tcp_type == TCP_TYPE_ACTIVE)
+ ABORT(R_INTERNAL);
+
+ if ((r=nr_socket_multi_tcp_get_sock_connected_to(mtcp_sock,addr,PREALLOC_CONNECT_NON_FRAMED,&nrsock)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if (_status)
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status);
+ return(_status);
+ }
+
+static int nr_socket_multi_tcp_destroy(void **objp)
+ {
+ nr_socket_multi_tcp *sock;
+ nr_tcp_socket_ctx *tcpsock;
+ NR_SOCKET fd;
+
+ if (!objp || !*objp)
+ return 0;
+
+ sock=(nr_socket_multi_tcp *)*objp;
+ *objp=0;
+
+ /* Cancel waiting on the socket */
+ if (sock->listen_socket && !nr_socket_getfd(sock->listen_socket, &fd)) {
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
+ }
+
+ nr_socket_destroy(&sock->listen_socket);
+
+ while (!TAILQ_EMPTY(&sock->sockets)) {
+
+ tcpsock = TAILQ_FIRST(&sock->sockets);
+ TAILQ_REMOVE(&sock->sockets, tcpsock, entry);
+
+ if (!nr_socket_getfd(tcpsock->inner, &fd)) {
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
+ }
+
+ nr_tcp_socket_ctx_destroy(&tcpsock);
+ }
+
+ RFREE(sock);
+
+ return 0;
+ }
+
+static int nr_socket_multi_tcp_sendto(void *obj, const void *msg, size_t len,
+ int flags, nr_transport_addr *to)
+ {
+ int r, _status;
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj;
+ nr_socket *nrsock;
+
+ if ((r=nr_socket_multi_tcp_get_sock_connected_to(sock, to,
+ PREALLOC_DONT_CONNECT_UNLESS_SO, &nrsock)))
+ ABORT(r);
+
+ if((r=nr_socket_sendto(nrsock, msg, len, flags, to)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if (_status)
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(to:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,to->as_string,_status);
+
+ return(_status);
+}
+
+static int nr_socket_multi_tcp_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags, nr_transport_addr *from)
+ {
+ int r, _status = 0;
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj;
+ nr_tcp_socket_ctx *tcpsock;
+
+ if (TAILQ_EMPTY(&sock->sockets))
+ ABORT(R_FAILED);
+
+ TAILQ_FOREACH(tcpsock, &sock->sockets, entry) {
+ if (nr_transport_addr_is_wildcard(&tcpsock->remote_addr))
+ continue;
+ r=nr_socket_recvfrom(tcpsock->inner, buf, maxlen, len, flags, from);
+ if (!r)
+ return 0;
+
+ if (r!=R_WOULDBLOCK) {
+ NR_SOCKET fd;
+ r_log(LOG_ICE,LOG_DEBUG,
+ "%s:%d function %s(to:%s) failed with error %d",__FILE__,
+ __LINE__,__FUNCTION__,tcpsock->remote_addr.as_string,r);
+ if (!nr_socket_getfd(tcpsock->inner, &fd)) {
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+ }
+
+ TAILQ_REMOVE(&sock->sockets, tcpsock, entry);
+ nr_tcp_socket_ctx_destroy(&tcpsock);
+ ABORT(r);
+ }
+ }
+
+ /* this also gets returned if all tcpsocks have wildcard remote_addr */
+ _status=R_WOULDBLOCK;
+ abort:
+
+ return(_status);
+ }
+
+static int nr_socket_multi_tcp_getaddr(void *obj, nr_transport_addr *addrp)
+ {
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj;
+
+ return nr_transport_addr_copy(addrp,&sock->addr);
+ }
+
+static int nr_socket_multi_tcp_close(void *obj)
+ {
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj;
+ nr_tcp_socket_ctx *tcpsock;
+
+ if(sock->listen_socket)
+ nr_socket_close(sock->listen_socket);
+
+ TAILQ_FOREACH(tcpsock, &sock->sockets, entry) {
+ nr_socket_close(tcpsock->inner); //ignore errors
+ }
+
+ return 0;
+ }
+
+static void nr_tcp_socket_readable_cb(NR_SOCKET s, int how, void *arg)
+ {
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)arg;
+
+ // rearm
+ NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, nr_tcp_socket_readable_cb, arg);
+
+ if (sock->readable_cb)
+ sock->readable_cb(s, how, sock->readable_cb_arg);
+ }
+
+static int nr_socket_multi_tcp_connect(void *obj, nr_transport_addr *addr)
+ {
+ int r, _status;
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj;
+ nr_socket *nrsock;
+
+ if ((r=nr_socket_multi_tcp_get_sock_connected_to(sock,addr,PREALLOC_CONNECT_FRAMED,&nrsock)))
+ ABORT(r);
+
+ _status=0;
+abort:
+ if (_status)
+ r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status);
+
+ return(_status);
+ }
+
+static void nr_tcp_multi_lsocket_readable_cb(NR_SOCKET s, int how, void *arg)
+ {
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)arg;
+ nr_socket *newsock;
+ nr_transport_addr remote_addr;
+ nr_tcp_socket_ctx *tcp_sock_ctx;
+ int r, _status;
+
+ // rearm
+ NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, nr_tcp_multi_lsocket_readable_cb, arg);
+
+ /* accept */
+ if ((r=nr_socket_accept(sock->listen_socket, &remote_addr, &newsock)))
+ ABORT(r);
+
+ /* This takes ownership of newsock whether it fails or not. */
+ if ((r=nr_tcp_socket_ctx_create(newsock, 1, sock->max_pending, &tcp_sock_ctx)))
+ ABORT(r);
+
+ nr_socket_buffered_set_connected_to(tcp_sock_ctx->inner, &remote_addr);
+
+ if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, &remote_addr, sock))) {
+ nr_tcp_socket_ctx_destroy(&tcp_sock_ctx);
+ ABORT(r);
+ }
+
+ TAILQ_INSERT_HEAD(&sock->sockets, tcp_sock_ctx, entry);
+
+ _status=0;
+abort:
+ if (_status) {
+ r_log(LOG_ICE,LOG_WARNING,"%s:%d %s failed to accept new TCP connection: %d",__FILE__,__LINE__,__FUNCTION__,_status);
+ } else {
+ r_log(LOG_ICE,LOG_INFO,"%s:%d %s accepted new TCP connection from %s",__FILE__,__LINE__,__FUNCTION__,remote_addr.as_string);
+ }
+ }
+
+static int nr_socket_multi_tcp_listen(void *obj, int backlog)
+ {
+ int r, _status;
+ nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj;
+ NR_SOCKET fd;
+
+ if(!sock->listen_socket)
+ ABORT(R_FAILED);
+
+ if ((r=nr_socket_listen(sock->listen_socket, backlog)))
+ ABORT(r);
+
+ if ((r=nr_socket_getfd(sock->listen_socket, &fd)))
+ ABORT(r);
+
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, nr_tcp_multi_lsocket_readable_cb, sock);
+
+ _status=0;
+ abort:
+ if (_status)
+ r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed with error %d",__FILE__,__LINE__,__FUNCTION__,_status);
+
+ return(_status);
+ }
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h
new file mode 100644
index 000000000..a99e1652d
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h
@@ -0,0 +1,52 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2014, Mozilla
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _nr_socket_multi_tcp_h
+#define _nr_socket_multi_tcp_h
+
+#include "nr_socket.h"
+
+/* Argument use_framing is 0 only in call from test code (STUN TCP server
+ listening socket). For other purposes it should be always set to true */
+
+int nr_socket_multi_tcp_create(struct nr_ice_ctx_ *ctx,
+ nr_transport_addr *addr, nr_socket_tcp_type tcp_type,
+ int precreated_so_count, int max_pending, nr_socket **sockp);
+
+int nr_socket_multi_tcp_set_readable_cb(nr_socket *sock,
+ NR_async_cb readable_cb,void *readable_cb_arg);
+
+int nr_socket_multi_tcp_stun_server_connect(nr_socket *sock,
+ nr_transport_addr *addr);
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c
new file mode 100644
index 000000000..0c9ec5674
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c
@@ -0,0 +1,82 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <nr_api.h>
+#include "nr_socket_wrapper.h"
+
+int nr_socket_wrapper_factory_create_int(void *obj, nr_socket_wrapper_factory_vtbl *vtbl,
+ nr_socket_wrapper_factory **wrapperp)
+{
+ int _status;
+ nr_socket_wrapper_factory *wrapper=0;
+
+ if (!(wrapper=RCALLOC(sizeof(nr_socket_wrapper_factory))))
+ ABORT(R_NO_MEMORY);
+
+ wrapper->obj=obj;
+ wrapper->vtbl=vtbl;
+
+ *wrapperp=wrapper;
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_socket_wrapper_factory_wrap(nr_socket_wrapper_factory *wrapper,
+ nr_socket *inner,
+ nr_socket **socketp)
+{
+ return wrapper->vtbl->wrap(wrapper->obj, inner, socketp);
+}
+
+int nr_socket_wrapper_factory_destroy(nr_socket_wrapper_factory **wrapperp)
+{
+ nr_socket_wrapper_factory *wrapper;
+
+ if (!wrapperp || !*wrapperp)
+ return 0;
+
+ wrapper = *wrapperp;
+ *wrapperp = 0;
+
+ assert(wrapper->vtbl);
+ if (wrapper->vtbl)
+ wrapper->vtbl->destroy(&wrapper->obj);
+
+ RFREE(wrapper);
+
+ return 0;
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h
new file mode 100644
index 000000000..717518e23
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h
@@ -0,0 +1,63 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _nr_socket_wrapper_h
+#define _nr_socket_wrapper_h
+
+#include "nr_socket.h"
+
+typedef struct nr_socket_wrapper_factory_vtbl_ {
+ int (*wrap)(void *obj,
+ nr_socket *socket,
+ nr_socket **socketp);
+ int (*destroy)(void **obj);
+} nr_socket_wrapper_factory_vtbl;
+
+typedef struct nr_socket_wrapper_factory_ {
+ void *obj;
+ nr_socket_wrapper_factory_vtbl *vtbl;
+} nr_socket_wrapper_factory;
+
+
+int nr_socket_wrapper_factory_create_int(void *obj, nr_socket_wrapper_factory_vtbl *vtbl,
+ nr_socket_wrapper_factory **wrapperp);
+
+
+int nr_socket_wrapper_factory_wrap(nr_socket_wrapper_factory *wrapper, nr_socket *inner,
+ nr_socket **socketp);
+
+int nr_socket_wrapper_factory_destroy(nr_socket_wrapper_factory **wrapperp);
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr.c b/media/mtransport/third_party/nICEr/src/net/transport_addr.c
new file mode 100644
index 000000000..b44ad5b2c
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.c
@@ -0,0 +1,476 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: transport_addr.c,v 1.2 2008/04/28 17:59:03 ekr Exp $";
+
+
+#include <csi_platform.h>
+#include <stdio.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <assert.h>
+#include "nr_api.h"
+#include "util.h"
+#include "transport_addr.h"
+
+int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr)
+ {
+ int _status;
+ /* Max length for normalized IPv6 address string representation is 39 */
+ char buffer[40];
+ const char *protocol;
+
+ switch(addr->protocol){
+ case IPPROTO_TCP:
+ protocol = "TCP";
+ break;
+ case IPPROTO_UDP:
+ protocol = "UDP";
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ switch(addr->ip_version){
+ case NR_IPV4:
+ if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer)))
+ strcpy(buffer, "[error]");
+ snprintf(addr->as_string,sizeof(addr->as_string),"IP4:%s:%d/%s",buffer,(int)ntohs(addr->u.addr4.sin_port),protocol);
+ break;
+ case NR_IPV6:
+ if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer)))
+ strcpy(buffer, "[error]");
+ snprintf(addr->as_string,sizeof(addr->as_string),"IP6:[%s]:%d/%s",buffer,(int)ntohs(addr->u.addr6.sin6_port),protocol);
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len)
+ {
+ int _status;
+ char buffer[40];
+
+ switch(addr->ip_version){
+ case NR_IPV4:
+ if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer))) {
+ strncpy(buffer, "[error]", len);
+ }
+ break;
+ case NR_IPV6:
+ if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer))) {
+ strncpy(buffer, "[error]", len);
+ }
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+ snprintf(buf,len,"%s:%s",addr->ifname,buffer);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr)
+ {
+ int r,_status;
+
+ if(!keep) memset(addr,0,sizeof(nr_transport_addr));
+
+ switch(protocol){
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ addr->protocol=protocol;
+
+ if(saddr->sa_family==AF_INET){
+ addr->ip_version=NR_IPV4;
+
+ memcpy(&addr->u.addr4,saddr,sizeof(struct sockaddr_in));
+ addr->addr=(struct sockaddr *)&addr->u.addr4;
+ addr->addr_len=sizeof(struct sockaddr_in);
+ }
+ else if(saddr->sa_family==AF_INET6){
+ addr->ip_version=NR_IPV6;
+
+ memcpy(&addr->u.addr6, saddr, sizeof(struct sockaddr_in6));
+ addr->addr=(struct sockaddr *)&addr->u.addr6;
+ addr->addr_len=sizeof(struct sockaddr_in6);
+ }
+ else
+ ABORT(R_BAD_ARGS);
+
+ if(r=nr_transport_addr_fmt_addr_string(addr))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from)
+ {
+ memcpy(to,from,sizeof(nr_transport_addr));
+ to->addr=(struct sockaddr *)((char *)to + ((char *)from->addr - (char *)from));
+
+ return(0);
+ }
+
+int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, nr_transport_addr *from)
+ {
+ int r,_status;
+ char save_ifname[MAXIFNAME];
+
+ strncpy(save_ifname, to->ifname, MAXIFNAME);
+ save_ifname[MAXIFNAME-1]=0; /* Ensure null termination */
+
+ if (r=nr_transport_addr_copy(to, from))
+ ABORT(r);
+
+ strncpy(to->ifname, save_ifname, MAXIFNAME);
+
+ if (r=nr_transport_addr_fmt_addr_string(to))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return _status;
+ }
+
+/* Convenience fxn. Is this the right API?*/
+int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr)
+ {
+ int r,_status;
+
+ memset(addr, 0, sizeof(nr_transport_addr));
+
+ addr->ip_version=NR_IPV4;
+ addr->protocol=protocol;
+#ifdef HAVE_SIN_LEN
+ addr->u.addr4.sin_len=sizeof(struct sockaddr_in);
+#endif
+ addr->u.addr4.sin_family=PF_INET;
+ addr->u.addr4.sin_port=htons(port);
+ addr->u.addr4.sin_addr.s_addr=htonl(ip4);
+ addr->addr=(struct sockaddr *)&addr->u.addr4;
+ addr->addr_len=sizeof(struct sockaddr_in);
+
+ if(r=nr_transport_addr_fmt_addr_string(addr))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_str_port_to_transport_addr(const char *ip, UINT2 port, int protocol, nr_transport_addr *addr_out)
+ {
+ int r,_status;
+ struct in_addr addr;
+ struct in6_addr addr6;
+
+ if (inet_pton(AF_INET, ip, &addr) == 1) {
+ if(r=nr_ip4_port_to_transport_addr(ntohl(addr.s_addr),port,protocol,addr_out))
+ ABORT(r);
+ } else if (inet_pton(AF_INET6, ip, &addr6) == 1) {
+ if(r=nr_ip6_port_to_transport_addr(&addr6,port,protocol,addr_out))
+ ABORT(r);
+ } else {
+ ABORT(R_BAD_DATA);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr)
+ {
+ int r,_status;
+
+ memset(addr, 0, sizeof(nr_transport_addr));
+
+ addr->ip_version=NR_IPV6;
+ addr->protocol=protocol;
+ addr->u.addr6.sin6_family=PF_INET6;
+ addr->u.addr6.sin6_port=htons(port);
+ memcpy(addr->u.addr6.sin6_addr.s6_addr, addr6->s6_addr, sizeof(addr6->s6_addr));
+ addr->addr=(struct sockaddr *)&addr->u.addr6;
+ addr->addr_len=sizeof(struct sockaddr_in6);
+
+ if(r=nr_transport_addr_fmt_addr_string(addr))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen)
+ {
+ int _status;
+ const char *res;
+
+ switch(addr->ip_version){
+ case NR_IPV4:
+ res = inet_ntop(AF_INET, &addr->u.addr4.sin_addr,str,maxlen);
+ break;
+ case NR_IPV6:
+ res = inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,str,maxlen);
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ if(!res){
+ if (errno == ENOSPC){
+ ABORT(R_BAD_ARGS);
+ }
+ ABORT(R_INTERNAL);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_transport_addr_get_port(nr_transport_addr *addr, int *port)
+ {
+ int _status;
+
+ switch(addr->ip_version){
+ case NR_IPV4:
+ *port=ntohs(addr->u.addr4.sin_port);
+ break;
+ case NR_IPV6:
+ *port=ntohs(addr->u.addr6.sin6_port);
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_transport_addr_set_port(nr_transport_addr *addr, int port)
+ {
+ int _status;
+
+ switch(addr->ip_version){
+ case NR_IPV4:
+ addr->u.addr4.sin_port=htons(port);
+ break;
+ case NR_IPV6:
+ addr->u.addr6.sin6_port=htons(port);
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* memcmp() may not work if, for instance, the string or interface
+ haven't been made. Hmmm.. */
+int nr_transport_addr_cmp(nr_transport_addr *addr1,nr_transport_addr *addr2,int mode)
+ {
+ assert(mode);
+
+ if(addr1->ip_version != addr2->ip_version)
+ return(1);
+
+ if(mode < NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL)
+ return(0);
+
+ if(addr1->protocol != addr2->protocol)
+ return(1);
+
+ if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ADDR)
+ return(0);
+
+ assert(addr1->addr_len == addr2->addr_len);
+ switch(addr1->ip_version){
+ case NR_IPV4:
+ if(addr1->u.addr4.sin_addr.s_addr != addr2->u.addr4.sin_addr.s_addr)
+ return(1);
+ if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ALL)
+ return(0);
+ if(addr1->u.addr4.sin_port != addr2->u.addr4.sin_port)
+ return(1);
+ break;
+ case NR_IPV6:
+ if(memcmp(addr1->u.addr6.sin6_addr.s6_addr,addr2->u.addr6.sin6_addr.s6_addr,sizeof(struct in6_addr)))
+ return(1);
+ if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ALL)
+ return(0);
+ if(addr1->u.addr6.sin6_port != addr2->u.addr6.sin6_port)
+ return(1);
+ break;
+ default:
+ abort();
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_is_loopback(nr_transport_addr *addr)
+ {
+ switch(addr->ip_version){
+ case NR_IPV4:
+ switch(addr->u.addr4.sin_family){
+ case AF_INET:
+ if (((ntohl(addr->u.addr4.sin_addr.s_addr)>>24)&0xff)==0x7f)
+ return 1;
+ break;
+ default:
+ UNIMPLEMENTED;
+ break;
+ }
+ break;
+
+ case NR_IPV6:
+ if(!memcmp(addr->u.addr6.sin6_addr.s6_addr,in6addr_loopback.s6_addr,sizeof(struct in6_addr)))
+ return(1);
+ break;
+ default:
+ UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_is_link_local(nr_transport_addr *addr)
+ {
+ switch(addr->ip_version){
+ case NR_IPV4:
+ /* RFC3927: 169.254/16 */
+ if ((ntohl(addr->u.addr4.sin_addr.s_addr) & 0xFFFF0000) == 0xA9FE0000)
+ return(1);
+ break;
+ case NR_IPV6:
+ {
+ UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr);
+ if ((*addrTop & htonl(0xFFC00000)) == htonl(0xFE800000))
+ return(2);
+ }
+ break;
+ default:
+ UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_is_wildcard(nr_transport_addr *addr)
+ {
+ switch(addr->ip_version){
+ case NR_IPV4:
+ if(addr->u.addr4.sin_addr.s_addr==INADDR_ANY)
+ return(1);
+ if(addr->u.addr4.sin_port==0)
+ return(1);
+ break;
+ case NR_IPV6:
+ if(!memcmp(addr->u.addr6.sin6_addr.s6_addr,in6addr_any.s6_addr,sizeof(struct in6_addr)))
+ return(1);
+ if(addr->u.addr6.sin6_port==0)
+ return(1);
+ break;
+ default:
+ UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+nr_transport_addr_mask nr_private_ipv4_addrs[] = {
+ /* RFC1918: 10/8 */
+ {0x0A000000, 0xFF000000},
+ /* RFC1918: 172.16/12 */
+ {0xAC100000, 0xFFF00000},
+ /* RFC1918: 192.168/16 */
+ {0xC0A80000, 0xFFFF0000},
+ /* RFC6598: 100.64/10 */
+ {0x64400000, 0xFFC00000}
+};
+
+int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr)
+ {
+ switch(addr->ip_version){
+ case NR_IPV4:
+ {
+ UINT4 ip = ntohl(addr->u.addr4.sin_addr.s_addr);
+ for (int i=0; i<(sizeof(nr_private_ipv4_addrs)/sizeof(nr_transport_addr_mask)); i++) {
+ if ((ip & nr_private_ipv4_addrs[i].mask) == nr_private_ipv4_addrs[i].addr)
+ return i + 1;
+ }
+ }
+ break;
+ case NR_IPV6:
+ return(0);
+ default:
+ UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_is_reliable_transport(nr_transport_addr *addr)
+ {
+ return addr->protocol == IPPROTO_TCP;
+ }
diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr.h b/media/mtransport/third_party/nICEr/src/net/transport_addr.h
new file mode 100644
index 000000000..dfec63329
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.h
@@ -0,0 +1,103 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _transport_addr_h
+#define _transport_addr_h
+
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+/* Length of a string hex representation of a MD5 hash */
+#define MAXIFNAME 33
+
+/* Generic transport address
+
+ This spans both sockaddr_in and sockaddr_in6
+ */
+typedef struct nr_transport_addr_ {
+ UCHAR ip_version; /* 4 or 6 */
+#define NR_IPV4 4
+#define NR_IPV6 6
+ UCHAR protocol; /* IPPROTO_TCP, IPPROTO_UDP */
+ struct sockaddr *addr;
+ int addr_len;
+ union {
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ } u;
+ char ifname[MAXIFNAME];
+ /* A string version.
+ 56 = 5 ("IP6:[") + 39 (ipv6 address) + 2 ("]:") + 5 (port) + 4 (/UDP) + 1 (null) */
+ char as_string[56];
+} nr_transport_addr;
+
+typedef struct nr_transport_addr_mask_ {
+ UINT4 addr;
+ UINT4 mask;
+} nr_transport_addr_mask;
+
+int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr);
+
+// addresses, ports in local byte order
+int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr);
+int nr_str_port_to_transport_addr(const char *str, UINT2 port, int protocol, nr_transport_addr *addr);
+int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr);
+
+int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen);
+int nr_transport_addr_get_port(nr_transport_addr *addr, int *port);
+int nr_transport_addr_cmp(nr_transport_addr *addr1,nr_transport_addr *addr2,int mode);
+#define NR_TRANSPORT_ADDR_CMP_MODE_VERSION 1
+#define NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL 2
+#define NR_TRANSPORT_ADDR_CMP_MODE_ADDR 3
+#define NR_TRANSPORT_ADDR_CMP_MODE_ALL 4
+
+int nr_transport_addr_is_wildcard(nr_transport_addr *addr);
+int nr_transport_addr_is_loopback(nr_transport_addr *addr);
+int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr);
+int nr_transport_addr_is_link_local(nr_transport_addr *addr);
+int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from);
+int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, nr_transport_addr *from);
+int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr);
+int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len);
+int nr_transport_addr_set_port(nr_transport_addr *addr, int port);
+int nr_transport_addr_is_reliable_transport(nr_transport_addr *addr);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.c b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.c
new file mode 100644
index 000000000..6a8a64bb9
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.c
@@ -0,0 +1,233 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: transport_addr_reg.c,v 1.2 2008/04/28 17:59:03 ekr Exp $";
+
+#include <csi_platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <strings.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <assert.h>
+#include "nr_api.h"
+#include "util.h"
+#include "transport_addr.h"
+#include "transport_addr_reg.h"
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46 /* Value used by linux/BSD */
+#endif
+
+int
+nr_reg_get_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr)
+{
+ int r,_status;
+ unsigned int count;
+ char *address = 0;
+ UINT2 port = 0;
+ char *ifname = 0;
+ char *protocol = 0;
+ int p;
+
+ if ((r=NR_reg_get_child_count(prefix, &count)))
+ ABORT(r);
+
+ if (count == 0)
+ ABORT(R_NOT_FOUND);
+
+ if ((r=NR_reg_alloc2_string(prefix, "address", &address))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ address = 0;
+ }
+
+ if ((r=NR_reg_alloc2_string(prefix, "ifname", &ifname))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ ifname = 0;
+ }
+
+ if ((r=NR_reg_get2_uint2(prefix, "port", &port))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ port = 0;
+ }
+
+ if ((r=NR_reg_alloc2_string(prefix, "protocol", &protocol))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ p = IPPROTO_UDP;
+
+ protocol = 0;
+ }
+ else {
+ if (!strcasecmp("tcp", protocol))
+ p = IPPROTO_TCP;
+ else if (!strcasecmp("udp", protocol))
+ p = IPPROTO_UDP;
+ else
+ ABORT(R_BAD_DATA);
+ }
+
+ if (!keep) memset(addr, 0, sizeof(*addr));
+
+ if ((r=nr_str_port_to_transport_addr(address?address:"0.0.0.0", port, p, addr)))
+ ABORT(r);
+
+ if (ifname)
+ strlcpy(addr->ifname, ifname, sizeof(addr->ifname));
+
+ _status=0;
+ abort:
+ RFREE(protocol);
+ RFREE(ifname);
+ RFREE(address);
+ return(_status);
+}
+
+int
+nr_reg_set_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr)
+{
+ int r,_status;
+
+ if (! keep) {
+ if ((r=NR_reg_del(prefix)))
+ ABORT(r);
+ }
+
+ switch (addr->ip_version) {
+ case NR_IPV4:
+ if (!nr_transport_addr_is_wildcard(addr)) {
+ if ((r=NR_reg_set2_string(prefix, "address", inet_ntoa(addr->u.addr4.sin_addr))))
+ ABORT(r);
+ }
+
+ if (addr->u.addr4.sin_port != 0) {
+ if ((r=NR_reg_set2_uint2(prefix, "port", ntohs(addr->u.addr4.sin_port))))
+ ABORT(r);
+ }
+ break;
+
+ case NR_IPV6:
+ if (!nr_transport_addr_is_wildcard(addr)) {
+ char address[INET6_ADDRSTRLEN];
+ if(!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,address,sizeof(address))) {
+ ABORT(R_BAD_DATA);
+ }
+
+ if ((r=NR_reg_set2_string(prefix, "address", address))) {
+ ABORT(r);
+ }
+ }
+
+ if (addr->u.addr6.sin6_port != 0) {
+ if ((r=NR_reg_set2_uint2(prefix, "port", ntohs(addr->u.addr6.sin6_port))))
+ ABORT(r);
+ }
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ /* We abort if neither NR_IPV4 or NR_IPV6 above */
+ switch (addr->protocol) {
+ case IPPROTO_TCP:
+ if ((r=NR_reg_set2_string(prefix, "protocol", "tcp")))
+ ABORT(r);
+ break;
+ case IPPROTO_UDP:
+ if ((r=NR_reg_set2_string(prefix, "protocol", "udp")))
+ ABORT(r);
+ break;
+ default:
+ UNIMPLEMENTED;
+ break;
+ }
+
+ if (strlen(addr->ifname) > 0) {
+ if ((r=NR_reg_set2_string(prefix, "ifname", addr->ifname)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ if (_status)
+ NR_reg_del(prefix);
+ return _status;
+}
+
+int
+nr_reg_get_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr)
+{
+ int r, _status;
+ NR_registry registry;
+
+ if ((r=NR_reg_make_registry(prefix, name, registry)))
+ ABORT(r);
+
+ if ((r=nr_reg_get_transport_addr(registry, keep, addr)))
+ ABORT(r);
+
+ _status = 0;
+abort:
+ return _status;
+}
+
+int
+nr_reg_set_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr)
+{
+ int r, _status;
+ NR_registry registry;
+
+ if ((r=NR_reg_make_registry(prefix, name, registry)))
+ ABORT(r);
+
+ if ((r=nr_reg_set_transport_addr(registry, keep, addr)))
+ ABORT(r);
+
+ _status = 0;
+abort:
+ return _status;
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.h b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.h
new file mode 100644
index 000000000..761953a9c
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.h
@@ -0,0 +1,46 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _transport_addr_reg_h
+#define _transport_addr_reg_h
+
+#include "registry.h"
+
+int nr_reg_get_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr);
+int nr_reg_set_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr);
+int nr_reg_get_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr);
+int nr_reg_set_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/addrs.c b/media/mtransport/third_party/nICEr/src/stun/addrs.c
new file mode 100644
index 000000000..c90191f73
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/addrs.c
@@ -0,0 +1,444 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: addrs.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <csi_platform.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <iphlpapi.h>
+#include <tchar.h>
+#else /* !WIN32 */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#ifndef ANDROID
+/* This works on linux and BSD, but not android */
+#include <sys/types.h> /* getifaddrs */
+#include <ifaddrs.h> /* getifaddrs */
+#else
+#include "ifaddrs-android.h"
+#define getifaddrs android_getifaddrs
+#define freeifaddrs android_freeifaddrs
+#endif
+
+#ifdef LINUX
+
+#ifdef ANDROID
+/* Work around an Android NDK < r8c bug */
+#undef __unused
+#else
+#include <linux/if.h> /* struct ifreq, IFF_POINTTOPOINT */
+#include <linux/wireless.h> /* struct iwreq */
+#include <linux/ethtool.h> /* struct ethtool_cmd */
+#include <linux/sockios.h> /* SIOCETHTOOL */
+#endif /* ANDROID */
+
+#endif /* LINUX */
+
+#endif /* !WIN32 */
+
+#include "stun.h"
+#include "addrs.h"
+#include "nr_crypto.h"
+#include "util.h"
+
+#if defined(WIN32)
+
+#define WIN32_MAX_NUM_INTERFACES 20
+
+#define NR_MD5_HASH_LENGTH 16
+
+#define _NR_MAX_KEY_LENGTH 256
+#define _NR_MAX_NAME_LENGTH 512
+
+#define _ADAPTERS_BASE_REG "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+static int nr_win32_get_adapter_friendly_name(char *adapter_GUID, char **friendly_name)
+{
+ int r,_status;
+ HKEY adapter_reg;
+ TCHAR adapter_key[_NR_MAX_KEY_LENGTH];
+ TCHAR keyval_buf[_NR_MAX_KEY_LENGTH];
+ TCHAR adapter_GUID_tchar[_NR_MAX_NAME_LENGTH];
+ DWORD keyval_len, key_type;
+ size_t converted_chars, newlen;
+ char *my_fn = 0;
+
+#ifdef _UNICODE
+ mbstowcs_s(&converted_chars, adapter_GUID_tchar, strlen(adapter_GUID)+1,
+ adapter_GUID, _TRUNCATE);
+#else
+ strlcpy(adapter_GUID_tchar, adapter_GUID, _NR_MAX_NAME_LENGTH);
+#endif
+
+ _tcscpy_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT(_ADAPTERS_BASE_REG));
+ _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\"));
+ _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, adapter_GUID_tchar);
+ _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\Connection"));
+
+ r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, adapter_key, 0, KEY_READ, &adapter_reg);
+
+ if (r != ERROR_SUCCESS) {
+ r_log(NR_LOG_STUN, LOG_ERR, "Got error %d opening adapter reg key\n", r);
+ ABORT(R_INTERNAL);
+ }
+
+ keyval_len = sizeof(keyval_buf);
+ r = RegQueryValueEx(adapter_reg, TEXT("Name"), NULL, &key_type,
+ (BYTE *)keyval_buf, &keyval_len);
+
+ RegCloseKey(adapter_reg);
+
+#ifdef UNICODE
+ newlen = wcslen(keyval_buf)+1;
+ my_fn = (char *) RCALLOC(newlen);
+ if (!my_fn) {
+ ABORT(R_NO_MEMORY);
+ }
+ wcstombs_s(&converted_chars, my_fn, newlen, keyval_buf, _TRUNCATE);
+#else
+ my_fn = r_strdup(keyval_buf);
+#endif
+
+ *friendly_name = my_fn;
+ _status=0;
+
+abort:
+ if (_status) {
+ if (my_fn) free(my_fn);
+ }
+ return(_status);
+}
+
+static int
+stun_get_win32_addrs(nr_local_addr addrs[], int maxaddrs, int *count)
+{
+ int r, _status;
+ PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL, tmpAddress = NULL;
+ // recomended per https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx
+ static const ULONG initialBufLen = 15000;
+ ULONG buflen = initialBufLen;
+ char bin_hashed_ifname[NR_MD5_HASH_LENGTH];
+ char hex_hashed_ifname[MAXIFNAME];
+ int n = 0;
+
+ *count = 0;
+
+ if (maxaddrs <= 0)
+ ABORT(R_BAD_ARGS);
+
+ /* According to MSDN (see above) we have try GetAdapterAddresses() multiple times */
+ for (n = 0; n < 5; n++) {
+ AdapterAddresses = (PIP_ADAPTER_ADDRESSES) RMALLOC(buflen);
+ if (AdapterAddresses == NULL) {
+ r_log(NR_LOG_STUN, LOG_ERR, "Error allocating buf for GetAdaptersAddresses()");
+ ABORT(R_NO_MEMORY);
+ }
+
+ r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, AdapterAddresses, &buflen);
+ if (r == NO_ERROR) {
+ break;
+ }
+ r_log(NR_LOG_STUN, LOG_ERR, "GetAdaptersAddresses() returned error (%d)", r);
+ RFREE(AdapterAddresses);
+ AdapterAddresses = NULL;
+ }
+
+ if (n >= 5) {
+ r_log(NR_LOG_STUN, LOG_ERR, "5 failures calling GetAdaptersAddresses()");
+ ABORT(R_INTERNAL);
+ }
+
+ n = 0;
+
+ /* Loop through the adapters */
+
+ for (tmpAddress = AdapterAddresses; tmpAddress != NULL; tmpAddress = tmpAddress->Next) {
+
+ if (tmpAddress->OperStatus != IfOperStatusUp)
+ continue;
+
+ if ((tmpAddress->IfIndex != 0) || (tmpAddress->Ipv6IfIndex != 0)) {
+ IP_ADAPTER_UNICAST_ADDRESS *u = 0;
+
+ if(r=nr_crypto_md5((UCHAR *)tmpAddress->FriendlyName,
+ wcslen(tmpAddress->FriendlyName) * sizeof(wchar_t),
+ bin_hashed_ifname))
+ ABORT(r);
+ if(r=nr_bin2hex(bin_hashed_ifname, sizeof(bin_hashed_ifname),
+ hex_hashed_ifname))
+ ABORT(r);
+
+ for (u = tmpAddress->FirstUnicastAddress; u != 0; u = u->Next) {
+ SOCKET_ADDRESS *sa_addr = &u->Address;
+
+ if ((sa_addr->lpSockaddr->sa_family == AF_INET) ||
+ (sa_addr->lpSockaddr->sa_family == AF_INET6)) {
+ if ((r=nr_sockaddr_to_transport_addr((struct sockaddr*)sa_addr->lpSockaddr, IPPROTO_UDP, 0, &(addrs[n].addr))))
+ ABORT(r);
+ }
+ else {
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized sa_family for address on adapter %lu", tmpAddress->IfIndex);
+ continue;
+ }
+
+ strlcpy(addrs[n].addr.ifname, hex_hashed_ifname, sizeof(addrs[n].addr.ifname));
+ if (tmpAddress->IfType == IF_TYPE_ETHERNET_CSMACD) {
+ addrs[n].interface.type = NR_INTERFACE_TYPE_WIRED;
+ } else if (tmpAddress->IfType == IF_TYPE_IEEE80211) {
+ /* Note: this only works for >= Win Vista */
+ addrs[n].interface.type = NR_INTERFACE_TYPE_WIFI;
+ } else {
+ addrs[n].interface.type = NR_INTERFACE_TYPE_UNKNOWN;
+ }
+#if (_WIN32_WINNT >= 0x0600)
+ /* Note: only >= Vista provide link speed information */
+ addrs[n].interface.estimated_speed = tmpAddress->TransmitLinkSpeed / 1000;
+#else
+ addrs[n].interface.estimated_speed = 0;
+#endif
+ if (++n >= maxaddrs)
+ goto done;
+ }
+ }
+ }
+
+ done:
+ *count = n;
+ _status = 0;
+
+ abort:
+ RFREE(AdapterAddresses);
+ return _status;
+}
+
+#else /* WIN32 */
+
+static int
+nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr);
+
+static int
+stun_getifaddrs(nr_local_addr addrs[], int maxaddrs, int *count)
+{
+ int r,_status;
+ struct ifaddrs* if_addrs_head=NULL;
+ struct ifaddrs* if_addr;
+
+ *count = 0;
+
+ if (maxaddrs <= 0)
+ ABORT(R_BAD_ARGS);
+
+ if (getifaddrs(&if_addrs_head) == -1) {
+ r_log(NR_LOG_STUN, LOG_ERR, "getifaddrs error e = %d", errno);
+ ABORT(R_INTERNAL);
+ }
+
+ if_addr = if_addrs_head;
+
+ while (if_addr && *count < maxaddrs) {
+ /* This can be null */
+ if (if_addr->ifa_addr) {
+ switch (if_addr->ifa_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ if (r=nr_sockaddr_to_transport_addr(if_addr->ifa_addr, IPPROTO_UDP, 0, &(addrs[*count].addr))) {
+ r_log(NR_LOG_STUN, LOG_ERR, "nr_sockaddr_to_transport_addr error r = %d", r);
+ } else {
+#if defined(LINUX) && !defined(ANDROID)
+ struct ethtool_cmd ecmd;
+ struct ifreq ifr;
+ struct iwreq wrq;
+ int e;
+ int s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ strncpy(ifr.ifr_name, if_addr->ifa_name, sizeof(ifr.ifr_name));
+ /* TODO (Bug 896851): interface property for Android */
+ /* Getting ethtool for ethernet information. */
+ ecmd.cmd = ETHTOOL_GSET;
+ /* In/out param */
+ ifr.ifr_data = (void*)&ecmd;
+
+ e = ioctl(s, SIOCETHTOOL, &ifr);
+ if (e == 0)
+ {
+ /* For wireless network, we won't get ethtool, it's a wired
+ * connection */
+ addrs[*count].interface.type = NR_INTERFACE_TYPE_WIRED;
+#ifdef DONT_HAVE_ETHTOOL_SPEED_HI
+ addrs[*count].interface.estimated_speed = ecmd.speed;
+#else
+ addrs[*count].interface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000;
+#endif
+ }
+
+ strncpy(wrq.ifr_name, if_addr->ifa_name, sizeof(wrq.ifr_name));
+ e = ioctl(s, SIOCGIWRATE, &wrq);
+ if (e == 0)
+ {
+ addrs[*count].interface.type = NR_INTERFACE_TYPE_WIFI;
+ addrs[*count].interface.estimated_speed = wrq.u.bitrate.value / 1000;
+ }
+
+ close(s);
+
+ if (if_addr->ifa_flags & IFF_POINTOPOINT)
+ {
+ addrs[*count].interface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN;
+ /* TODO (Bug 896913): find backend network type of this VPN */
+ }
+#else
+ addrs[*count].interface.type = NR_INTERFACE_TYPE_UNKNOWN;
+ addrs[*count].interface.estimated_speed = 0;
+#endif
+ strlcpy(addrs[*count].addr.ifname, if_addr->ifa_name, sizeof(addrs[*count].addr.ifname));
+ ++(*count);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ if_addr = if_addr->ifa_next;
+ }
+
+ _status=0;
+abort:
+ if (if_addrs_head) {
+ freeifaddrs(if_addrs_head);
+ }
+ return(_status);
+}
+
+#endif
+
+static int
+nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr)
+{
+ int i;
+ int different;
+
+ for (i = 0; i < count; ++i) {
+ different = nr_transport_addr_cmp(&addrs[i].addr, &(addr->addr),
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL);
+ if (!different)
+ return 1; /* duplicate */
+ }
+
+ return 0;
+}
+
+int
+nr_stun_remove_duplicate_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count)
+{
+ int r, _status;
+ nr_local_addr *tmp = 0;
+ int i;
+ int n;
+
+ tmp = RMALLOC(*count * sizeof(*tmp));
+ if (!tmp)
+ ABORT(R_NO_MEMORY);
+
+ n = 0;
+ for (i = 0; i < *count; ++i) {
+ if (nr_stun_is_duplicate_addr(tmp, n, &addrs[i])) {
+ /* skip addrs[i], it's a duplicate */
+ }
+ else if (remove_loopback && nr_transport_addr_is_loopback(&addrs[i].addr)) {
+ /* skip addrs[i], it's a loopback */
+ }
+ else if (remove_link_local &&
+ addrs[i].addr.ip_version == NR_IPV6 &&
+ nr_transport_addr_is_link_local(&addrs[i].addr)) {
+ /* skip addrs[i], it's a link-local address */
+ }
+ else {
+ /* otherwise, copy it to the temporary array */
+ if ((r=nr_local_addr_copy(&tmp[n], &addrs[i])))
+ ABORT(r);
+ ++n;
+ }
+ }
+
+ *count = n;
+
+ memset(addrs, 0, *count * sizeof(*addrs));
+ /* copy temporary array into passed in/out array */
+ for (i = 0; i < *count; ++i) {
+ if ((r=nr_local_addr_copy(&addrs[i], &tmp[i])))
+ ABORT(r);
+ }
+
+ _status = 0;
+ abort:
+ RFREE(tmp);
+ return _status;
+}
+
+#ifndef USE_PLATFORM_NR_STUN_GET_ADDRS
+
+int
+nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int drop_loopback, int drop_link_local, int *count)
+{
+ int r,_status=0;
+ int i;
+ char typestr[100];
+
+#ifdef WIN32
+ _status = stun_get_win32_addrs(addrs, maxaddrs, count);
+#else
+ _status = stun_getifaddrs(addrs, maxaddrs, count);
+#endif
+
+ if ((r=nr_stun_remove_duplicate_addrs(addrs, drop_loopback, drop_link_local, count)))
+ ABORT(r);
+
+ for (i = 0; i < *count; ++i) {
+ nr_local_addr_fmt_info_string(addrs+i,typestr,sizeof(typestr));
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s, type: %s\n",
+ i,addrs[i].addr.as_string,addrs[i].addr.ifname,typestr);
+ }
+
+abort:
+ return _status;
+}
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/stun/addrs.h b/media/mtransport/third_party/nICEr/src/stun/addrs.h
new file mode 100644
index 000000000..61a3496d1
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/addrs.h
@@ -0,0 +1,43 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef _addrs_h_
+#define _addrs_h_
+
+#include "transport_addr.h"
+#include "local_addr.h"
+
+int nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int remove_loopback, int remove_link_local, int *count);
+int nr_stun_remove_duplicate_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count);
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.c b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.c
new file mode 100644
index 000000000..d7d390f24
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.c
@@ -0,0 +1,242 @@
+/*
+Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of Google nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#if defined(ANDROID)
+#include "ifaddrs-android.h"
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct netlinkrequest {
+ struct nlmsghdr header;
+ struct ifaddrmsg msg;
+};
+
+static const int kMaxReadSize = 4096;
+
+static int set_ifname(struct ifaddrs* ifaddr, int interface) {
+ char buf[IFNAMSIZ] = {0};
+ char* name = if_indextoname(interface, buf);
+ if (name == NULL) {
+ return -1;
+ }
+ ifaddr->ifa_name = malloc(strlen(name) + 1);
+ strncpy(ifaddr->ifa_name, name, strlen(name) + 1);
+ return 0;
+}
+
+static int set_flags(struct ifaddrs* ifaddr) {
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1);
+ int rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ close(fd);
+ if (rc == -1) {
+ return -1;
+ }
+ ifaddr->ifa_flags = ifr.ifr_flags;
+ return 0;
+}
+
+static int set_addresses(struct ifaddrs* ifaddr, struct ifaddrmsg* msg, void* data,
+ size_t len) {
+ if (msg->ifa_family == AF_INET) {
+ struct sockaddr_in* sa = malloc(sizeof(struct sockaddr_in));
+ memset(sa, 0, sizeof(struct sockaddr_in));
+ sa->sin_family = AF_INET;
+ memcpy(&sa->sin_addr, data, len);
+ ifaddr->ifa_addr = (struct sockaddr*)sa;
+ } else if (msg->ifa_family == AF_INET6) {
+ struct sockaddr_in6* sa = malloc(sizeof(struct sockaddr_in6));
+ memset(sa, 0, sizeof(struct sockaddr_in6));
+ sa->sin6_family = AF_INET6;
+ sa->sin6_scope_id = msg->ifa_index;
+ memcpy(&sa->sin6_addr, data, len);
+ ifaddr->ifa_addr = (struct sockaddr*)sa;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+static int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) {
+ char* prefix = NULL;
+ if (family == AF_INET) {
+ struct sockaddr_in* mask = malloc(sizeof(struct sockaddr_in));
+ memset(mask, 0, sizeof(struct sockaddr_in));
+ mask->sin_family = AF_INET;
+ memset(&mask->sin_addr, 0, sizeof(struct in_addr));
+ ifaddr->ifa_netmask = (struct sockaddr*)mask;
+ if (prefixlen > 32) {
+ prefixlen = 32;
+ }
+ prefix = (char*)&mask->sin_addr;
+ } else if (family == AF_INET6) {
+ struct sockaddr_in6* mask = malloc(sizeof(struct sockaddr_in6));
+ memset(mask, 0, sizeof(struct sockaddr_in6));
+ mask->sin6_family = AF_INET6;
+ memset(&mask->sin6_addr, 0, sizeof(struct in6_addr));
+ ifaddr->ifa_netmask = (struct sockaddr*)mask;
+ if (prefixlen > 128) {
+ prefixlen = 128;
+ }
+ prefix = (char*)&mask->sin6_addr;
+ } else {
+ return -1;
+ }
+ for (int i = 0; i < (prefixlen / 8); i++) {
+ *prefix++ = 0xFF;
+ }
+ char remainder = 0xff;
+ remainder <<= (8 - prefixlen % 8);
+ *prefix = remainder;
+ return 0;
+}
+
+static int populate_ifaddrs(struct ifaddrs* ifaddr, struct ifaddrmsg* msg, void* bytes,
+ size_t len) {
+ if (set_ifname(ifaddr, msg->ifa_index) != 0) {
+ return -1;
+ }
+ if (set_flags(ifaddr) != 0) {
+ return -1;
+ }
+ if (set_addresses(ifaddr, msg, bytes, len) != 0) {
+ return -1;
+ }
+ if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int android_getifaddrs(struct ifaddrs** result) {
+ int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (fd < 0) {
+ return -1;
+ }
+
+ struct netlinkrequest ifaddr_request;
+ memset(&ifaddr_request, 0, sizeof(ifaddr_request));
+ ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
+ ifaddr_request.header.nlmsg_type = RTM_GETADDR;
+ ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+
+ ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0);
+ if ((size_t)count != ifaddr_request.header.nlmsg_len) {
+ close(fd);
+ return -1;
+ }
+ struct ifaddrs* start = NULL;
+ struct ifaddrs* current = NULL;
+ char buf[kMaxReadSize];
+ ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0);
+ while (amount_read > 0) {
+ struct nlmsghdr* header = (struct nlmsghdr*)&buf[0];
+ size_t header_size = (size_t)amount_read;
+ for ( ; NLMSG_OK(header, header_size);
+ header = NLMSG_NEXT(header, header_size)) {
+ switch (header->nlmsg_type) {
+ case NLMSG_DONE:
+ /* Success. Return. */
+ *result = start;
+ close(fd);
+ return 0;
+ case NLMSG_ERROR:
+ close(fd);
+ android_freeifaddrs(start);
+ return -1;
+ case RTM_NEWADDR: {
+ struct ifaddrmsg* address_msg =
+ (struct ifaddrmsg*)NLMSG_DATA(header);
+ struct rtattr* rta = IFA_RTA(address_msg);
+ ssize_t payload_len = IFA_PAYLOAD(header);
+ while (RTA_OK(rta, payload_len)) {
+ if (rta->rta_type == IFA_ADDRESS) {
+ int family = address_msg->ifa_family;
+ if (family == AF_INET || family == AF_INET6) {
+ struct ifaddrs* newest = malloc(sizeof(struct ifaddrs));
+ memset(newest, 0, sizeof(struct ifaddrs));
+ if (current) {
+ current->ifa_next = newest;
+ } else {
+ start = newest;
+ }
+ if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta),
+ RTA_PAYLOAD(rta)) != 0) {
+ android_freeifaddrs(start);
+ *result = NULL;
+ return -1;
+ }
+ current = newest;
+ }
+ }
+ rta = RTA_NEXT(rta, payload_len);
+ }
+ break;
+ }
+ }
+ }
+ amount_read = recv(fd, &buf, kMaxReadSize, 0);
+ }
+ close(fd);
+ android_freeifaddrs(start);
+ return -1;
+}
+
+void android_freeifaddrs(struct ifaddrs* addrs) {
+ struct ifaddrs* last = NULL;
+ struct ifaddrs* cursor = addrs;
+ while (cursor) {
+ free(cursor->ifa_name);
+ free(cursor->ifa_addr);
+ free(cursor->ifa_netmask);
+ last = cursor;
+ cursor = cursor->ifa_next;
+ free(last);
+ }
+}
+
+#endif /* defined(ANDROID) */
diff --git a/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.h b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.h
new file mode 100644
index 000000000..5ba174ce6
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.h
@@ -0,0 +1,57 @@
+/*
+Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of Google nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef WEBRTC_BASE_IFADDRS_ANDROID_H_
+#define WEBRTC_BASE_IFADDRS_ANDROID_H_
+
+#include <stdio.h>
+#include <sys/socket.h>
+
+/* Implementation of getifaddrs for Android.
+ * Fills out a list of ifaddr structs (see below) which contain information
+ * about every network interface available on the host.
+ * See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function). */
+struct ifaddrs {
+ struct ifaddrs* ifa_next;
+ char* ifa_name;
+ unsigned int ifa_flags;
+ struct sockaddr* ifa_addr;
+ struct sockaddr* ifa_netmask;
+ /* Real ifaddrs has broadcast, point to point and data members.
+ * We don't need them (yet?). */
+};
+
+int android_getifaddrs(struct ifaddrs** result);
+void android_freeifaddrs(struct ifaddrs* addrs);
+
+#endif /* WEBRTC_BASE_IFADDRS_ANDROID_H_ */
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
new file mode 100644
index 000000000..c82c545f3
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
@@ -0,0 +1,610 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <nr_api.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#include "p_buf.h"
+#include "nr_socket.h"
+#include "stun.h"
+#include "nr_socket_buffered_stun.h"
+
+#define NR_MAX_FRAME_SIZE 0xFFFF
+
+typedef struct nr_frame_header_ {
+ UINT2 frame_length;
+ char data[0];
+} nr_frame_header;
+
+typedef struct nr_socket_buffered_stun_ {
+ nr_socket *inner;
+ nr_transport_addr remote_addr;
+ int connected;
+
+ /* Read state */
+ int read_state;
+#define NR_ICE_SOCKET_READ_NONE 0
+#define NR_ICE_SOCKET_READ_HDR 1
+#define NR_ICE_SOCKET_READ_FAILED 2
+ UCHAR *buffer;
+ size_t buffer_size;
+ size_t bytes_needed;
+ size_t bytes_read;
+ NR_async_cb readable_cb;
+ void *readable_cb_arg;
+
+ /* Write state */
+ nr_p_buf_ctx *p_bufs;
+ nr_p_buf_head pending_writes;
+ size_t pending;
+ size_t max_pending;
+ nr_framing_type framing_type;
+} nr_socket_buffered_stun;
+
+static int nr_socket_buffered_stun_destroy(void **objp);
+static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len,
+ int flags, nr_transport_addr *to);
+static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
+static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_buffered_stun_close(void *obj);
+static int nr_socket_buffered_stun_connect(void *sock, nr_transport_addr *addr);
+static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written);
+static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg);
+static int nr_socket_buffered_stun_listen(void *obj, int backlog);
+static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp);
+
+static nr_socket_vtbl nr_socket_buffered_stun_vtbl={
+ 2,
+ nr_socket_buffered_stun_destroy,
+ nr_socket_buffered_stun_sendto,
+ nr_socket_buffered_stun_recvfrom,
+ nr_socket_buffered_stun_getfd,
+ nr_socket_buffered_stun_getaddr,
+ nr_socket_buffered_stun_connect,
+ 0,
+ 0,
+ nr_socket_buffered_stun_close,
+ nr_socket_buffered_stun_listen,
+ nr_socket_buffered_stun_accept
+};
+
+void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock,
+ NR_async_cb readable_cb, void *readable_cb_arg)
+{
+ nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj;
+
+ buf_sock->readable_cb = readable_cb;
+ buf_sock->readable_cb_arg = readable_cb_arg;
+}
+
+int nr_socket_buffered_set_connected_to(nr_socket *sock, nr_transport_addr *remote_addr)
+{
+ nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj;
+ int r, _status;
+
+ if ((r=nr_transport_addr_copy(&buf_sock->remote_addr, remote_addr)))
+ ABORT(r);
+
+ buf_sock->connected = 1;
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending,
+ nr_framing_type framing_type, nr_socket **sockp)
+{
+ int r, _status;
+ nr_socket_buffered_stun *sock = 0;
+ size_t frame_size;
+
+ if (!(sock = RCALLOC(sizeof(nr_socket_buffered_stun))))
+ ABORT(R_NO_MEMORY);
+
+ sock->inner = inner;
+ sock->framing_type = framing_type;
+
+ if ((r=nr_ip4_port_to_transport_addr(INADDR_ANY, 0, IPPROTO_UDP, &sock->remote_addr)))
+ ABORT(r);
+
+ switch (framing_type) {
+ case ICE_TCP_FRAMING:
+ frame_size = sizeof(nr_frame_header);
+ sock->buffer_size = sizeof(nr_frame_header) + NR_MAX_FRAME_SIZE;
+ sock->bytes_needed = sizeof(nr_frame_header);
+ break;
+ case TURN_TCP_FRAMING:
+ frame_size = 0;
+ sock->buffer_size = NR_STUN_MAX_MESSAGE_SIZE;
+ sock->bytes_needed = sizeof(nr_stun_message_header);
+ break;
+ default:
+ assert(0);
+ ABORT(R_BAD_ARGS);
+ }
+
+ /* TODO(ekr@rtfm.com): Check this */
+ if (!(sock->buffer = RMALLOC(sock->buffer_size)))
+ ABORT(R_NO_MEMORY);
+
+ sock->read_state = NR_ICE_SOCKET_READ_NONE;
+ sock->connected = 0;
+
+ STAILQ_INIT(&sock->pending_writes);
+ if ((r=nr_p_buf_ctx_create(sock->buffer_size, &sock->p_bufs)))
+ ABORT(r);
+ sock->max_pending = max_pending + frame_size;
+
+ if ((r=nr_socket_create_int(sock, &nr_socket_buffered_stun_vtbl, sockp)))
+ ABORT(r);
+
+ _status=0;
+abort:
+ if (_status && sock) {
+ void *sock_v = sock;
+ sock->inner = 0; /* Give up ownership so we don't destroy */
+ nr_socket_buffered_stun_destroy(&sock_v);
+ }
+ return(_status);
+}
+
+/* Note: This destroys the inner socket */
+int nr_socket_buffered_stun_destroy(void **objp)
+{
+ nr_socket_buffered_stun *sock;
+ NR_SOCKET fd;
+
+ if (!objp || !*objp)
+ return 0;
+
+ sock = (nr_socket_buffered_stun *)*objp;
+ *objp = 0;
+
+ /* Free the buffer if needed */
+ RFREE(sock->buffer);
+
+ /* Cancel waiting on the socket */
+ if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) {
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+ }
+
+ nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes);
+ nr_p_buf_ctx_destroy(&sock->p_bufs);
+ nr_socket_destroy(&sock->inner);
+ RFREE(sock);
+
+ return 0;
+}
+
+static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len,
+ int flags, nr_transport_addr *to)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+ int r, _status;
+ size_t written;
+ nr_frame_header *frame = NULL;
+
+ /* Check that we are writing to the connected address if
+ connected */
+ if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) {
+ if (nr_transport_addr_cmp(&sock->remote_addr, to, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ r_log(LOG_GENERIC, LOG_ERR, "Sendto on connected socket doesn't match");
+ ABORT(R_BAD_DATA);
+ }
+ }
+
+ if (sock->framing_type == ICE_TCP_FRAMING) {
+
+ assert(len <= NR_MAX_FRAME_SIZE);
+ if (len > NR_MAX_FRAME_SIZE)
+ ABORT(R_FAILED);
+
+ if (!(frame = RMALLOC(len + sizeof(nr_frame_header))))
+ ABORT(R_NO_MEMORY);
+
+ frame->frame_length = htons(len);
+ memcpy(frame->data, msg, len);
+ len += sizeof(nr_frame_header);
+ msg = frame;
+ }
+
+ if ((r=nr_socket_buffered_stun_write(obj, msg, len, &written)))
+ ABORT(r);
+
+ if (len != written)
+ ABORT(R_IO_ERROR);
+
+ _status=0;
+abort:
+ RFREE(frame);
+ return _status;
+}
+
+static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock)
+ {
+ NR_SOCKET fd;
+
+ sock->read_state = NR_ICE_SOCKET_READ_FAILED;
+
+ /* Cancel waiting on the socket */
+ if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) {
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
+ }
+ }
+
+static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags, nr_transport_addr *from)
+{
+ int r, _status;
+ size_t bytes_read;
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+ nr_frame_header *frame = (nr_frame_header *)sock->buffer;
+ size_t skip_hdr_size = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : 0;
+
+ if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) {
+ ABORT(R_FAILED);
+ }
+
+ while (sock->bytes_needed) {
+ /* Read all the expected bytes */
+ assert(sock->bytes_needed <= sock->buffer_size - sock->bytes_read);
+
+ if(r=nr_socket_read(sock->inner,
+ sock->buffer + sock->bytes_read,
+ sock->bytes_needed, &bytes_read, 0))
+ ABORT(r);
+
+ assert(bytes_read <= sock->bytes_needed);
+ sock->bytes_needed -= bytes_read;
+ sock->bytes_read += bytes_read;
+
+ /* Unfinished */
+ if (sock->bytes_needed)
+ ABORT(R_WOULDBLOCK);
+
+ /* No more bytes expected */
+ if (sock->read_state == NR_ICE_SOCKET_READ_NONE) {
+ size_t remaining_length;
+ if (sock->framing_type == ICE_TCP_FRAMING) {
+ if (sock->bytes_read < sizeof(nr_frame_header))
+ ABORT(R_BAD_DATA);
+ remaining_length = ntohs(frame->frame_length);
+ } else {
+ int tmp_length;
+
+ /* Parse the header */
+ if (r = nr_stun_message_length(sock->buffer, sock->bytes_read, &tmp_length))
+ ABORT(r);
+ assert(tmp_length >= 0);
+ if (tmp_length < 0)
+ ABORT(R_BAD_DATA);
+ remaining_length = tmp_length;
+
+ }
+ /* Check to see if we have enough room */
+ if ((sock->buffer_size - sock->bytes_read) < remaining_length)
+ ABORT(R_BAD_DATA);
+
+ sock->read_state = NR_ICE_SOCKET_READ_HDR;
+ /* Set ourselves up to read the rest of the data */
+ sock->bytes_needed = remaining_length;
+ }
+ }
+
+ assert(skip_hdr_size <= sock->bytes_read);
+ if (skip_hdr_size > sock->bytes_read)
+ ABORT(R_BAD_DATA);
+ sock->bytes_read -= skip_hdr_size;
+
+ if (maxlen < sock->bytes_read)
+ ABORT(R_BAD_ARGS);
+
+ *len = sock->bytes_read;
+ memcpy(buf, sock->buffer + skip_hdr_size, sock->bytes_read);
+
+ sock->bytes_read = 0;
+ sock->read_state = NR_ICE_SOCKET_READ_NONE;
+ sock->bytes_needed = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : sizeof(nr_stun_message_header);
+
+ assert(!nr_transport_addr_is_wildcard(&sock->remote_addr));
+ if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) {
+ if ((r=nr_transport_addr_copy(from, &sock->remote_addr)))
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ if (_status && (_status != R_WOULDBLOCK)) {
+ nr_socket_buffered_stun_failed(sock);
+ }
+
+ return(_status);
+}
+
+static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+ return nr_socket_getfd(sock->inner, fd);
+}
+
+static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+ return nr_socket_getaddr(sock->inner, addrp);
+}
+
+static int nr_socket_buffered_stun_close(void *obj)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+ NR_SOCKET fd;
+
+ /* Cancel waiting on the socket */
+ if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) {
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+ }
+
+ return nr_socket_close(sock->inner);
+}
+
+static int nr_socket_buffered_stun_listen(void *obj, int backlog)
+{
+ int r, _status;
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+
+ if (!sock->inner)
+ ABORT(R_FAILED);
+
+ if ((r=nr_socket_listen(sock->inner, backlog)))
+ ABORT(r);
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+
+static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp)
+{
+ nr_socket_buffered_stun *bsock = (nr_socket_buffered_stun *)obj;
+
+ return nr_socket_accept(bsock->inner, addrp, sockp);
+}
+
+static void nr_socket_buffered_stun_connected_cb(NR_SOCKET s, int how, void *arg)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg;
+ int r, _status;
+ NR_SOCKET fd;
+
+ assert(!sock->connected);
+
+ sock->connected = 1;
+
+ if ((r=nr_socket_getfd(sock->inner, &fd)))
+ ABORT(r);
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+
+ // once connected arm for read
+ if (sock->readable_cb) {
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, sock->readable_cb, sock->readable_cb_arg);
+ }
+
+ if (sock->pending) {
+ r_log(LOG_GENERIC, LOG_INFO, "Invoking writable_cb on connected (%u)", (uint32_t) sock->pending);
+ nr_socket_buffered_stun_writable_cb(s, how, arg);
+ }
+
+ _status=0;
+abort:
+ if (_status) {
+ r_log(LOG_GENERIC, LOG_ERR, "Failure in nr_socket_buffered_stun_connected_cb: %d", _status);
+
+ }
+}
+
+static int nr_socket_buffered_stun_connect(void *obj, nr_transport_addr *addr)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+ int r, _status;
+
+ if ((r=nr_transport_addr_copy(&sock->remote_addr, addr)))
+ ABORT(r);
+
+ if ((r=nr_socket_connect(sock->inner, addr))) {
+ if (r == R_WOULDBLOCK) {
+ NR_SOCKET fd;
+
+ if ((r=nr_socket_getfd(sock->inner, &fd)))
+ ABORT(r);
+
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_connected_cb, sock);
+ ABORT(R_WOULDBLOCK);
+ }
+ ABORT(r);
+ } else {
+ r_log(LOG_GENERIC, LOG_INFO, "Connected without blocking");
+ sock->connected = 1;
+ }
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static int nr_socket_buffered_stun_arm_writable_cb(nr_socket_buffered_stun *sock)
+{
+ int r, _status;
+ NR_SOCKET fd;
+
+ if ((r=nr_socket_getfd(sock->inner, &fd)))
+ ABORT(r);
+
+ NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_writable_cb, sock);
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj;
+ int already_armed = 0;
+ int r,_status;
+ size_t written2 = 0;
+ size_t original_len = len;
+
+ /* Buffers are close to full, report error. Do this now so we never
+ get partial writes */
+ if ((sock->pending + len) > sock->max_pending) {
+ r_log(LOG_GENERIC, LOG_INFO, "Write buffer for %s full (%u + %u > %u) - re-arming @%p",
+ sock->remote_addr.as_string, (uint32_t)sock->pending, (uint32_t)len, (uint32_t)sock->max_pending,
+ &(sock->pending));
+ ABORT(R_WOULDBLOCK);
+ }
+
+
+ if (sock->connected && !sock->pending) {
+ r = nr_socket_write(sock->inner, msg, len, &written2, 0);
+ if (r) {
+ if (r != R_WOULDBLOCK) {
+ r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d",
+ sock->remote_addr.as_string, r);
+ ABORT(r);
+ }
+ r_log(LOG_GENERIC, LOG_INFO, "Write of %" PRIu64 " blocked for %s",
+ (uint64_t) len, sock->remote_addr.as_string);
+
+ written2=0;
+ }
+ } else {
+ already_armed = 1;
+ }
+
+ /* Buffer what's left */
+ len -= written2;
+
+ if (len) {
+ if ((r=nr_p_buf_write_to_chain(sock->p_bufs, &sock->pending_writes,
+ ((UCHAR *)msg) + written2, len))) {
+ r_log(LOG_GENERIC, LOG_ERR, "Write_to_chain error for %s - %d",
+ sock->remote_addr.as_string, r);
+
+ ABORT(r);
+ }
+
+ sock->pending += len;
+ }
+
+ if (sock->pending) {
+ if (!already_armed) {
+ if ((r=nr_socket_buffered_stun_arm_writable_cb(sock)))
+ ABORT(r);
+ }
+ r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s %u - %s armed (@%p),%s connected",
+ sock->remote_addr.as_string, (uint32_t)sock->pending,
+ already_armed ? "already" : "", &sock->pending,
+ sock->connected ? "" : " not");
+ }
+
+ *written = original_len;
+
+ _status=0;
+abort:
+ return _status;
+}
+
+static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg)
+{
+ nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg;
+ int r,_status;
+ nr_p_buf *n1, *n2;
+
+ if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) {
+ ABORT(R_FAILED);
+ }
+
+ /* Try to flush */
+ STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) {
+ size_t written = 0;
+
+ if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset,
+ n1->length - n1->r_offset,
+ &written, 0))) {
+
+ r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d",
+ sock->remote_addr.as_string, r);
+ ABORT(r);
+ }
+
+ n1->r_offset += written;
+ assert(sock->pending >= written);
+ sock->pending -= written;
+
+ if (n1->r_offset < n1->length) {
+ /* We wrote something, but not everything */
+ r_log(LOG_GENERIC, LOG_INFO, "Write in callback didn't write all (remaining %u of %u) for %s",
+ n1->length - n1->r_offset, n1->length,
+ sock->remote_addr.as_string);
+ ABORT(R_WOULDBLOCK);
+ }
+
+ /* We are done with this p_buf */
+ STAILQ_REMOVE_HEAD(&sock->pending_writes, entry);
+ nr_p_buf_free(sock->p_bufs, n1);
+ }
+
+ assert(!sock->pending);
+ _status=0;
+abort:
+ r_log(LOG_GENERIC, LOG_INFO, "Writable_cb %s (%u (%p) pending)",
+ sock->remote_addr.as_string, (uint32_t)sock->pending, &(sock->pending));
+ if (_status && _status != R_WOULDBLOCK) {
+ r_log(LOG_GENERIC, LOG_ERR, "Failure in writable_cb: %d", _status);
+ nr_socket_buffered_stun_failed(sock);
+ } else if (sock->pending) {
+ nr_socket_buffered_stun_arm_writable_cb(sock);
+ }
+}
diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
new file mode 100644
index 000000000..fa65e4b08
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
@@ -0,0 +1,64 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _nr_socket_buffered_stun_h
+#define _nr_socket_buffered_stun_h
+
+#include "nr_socket.h"
+
+/* Wrapper socket which provides buffered STUN-oriented I/O
+
+ 1. Writes don't block and are automatically flushed when needed.
+ 2. All reads are in units of STUN messages
+
+ This socket takes ownership of the inner socket |sock|.
+ */
+
+typedef enum {
+ TURN_TCP_FRAMING=0,
+ ICE_TCP_FRAMING
+} nr_framing_type;
+
+void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock,
+ NR_async_cb readable_cb, void *readable_cb_arg);
+
+int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending,
+ nr_framing_type framing_type, nr_socket **sockp);
+
+int nr_socket_buffered_set_connected_to(nr_socket *sock,
+ nr_transport_addr *remote_addr);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c
new file mode 100644
index 000000000..a12966625
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c
@@ -0,0 +1,198 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: nr_socket_turn.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#ifdef USE_TURN
+
+#include <csi_platform.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include "stun.h"
+#include "turn_client_ctx.h"
+#include "nr_socket_turn.h"
+
+
+static char *nr_socket_turn_magic_cookie = "nr_socket_turn";
+
+typedef struct nr_socket_turn_ {
+ char *magic_cookie;
+ nr_turn_client_ctx *turn;
+} nr_socket_turn;
+
+
+static int nr_socket_turn_destroy(void **objp);
+static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len,
+ int flags, nr_transport_addr *to);
+static int nr_socket_turn_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags, nr_transport_addr *from);
+static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd);
+static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp);
+static int nr_socket_turn_close(void *obj);
+
+static nr_socket_vtbl nr_socket_turn_vtbl={
+ 2,
+ nr_socket_turn_destroy,
+ nr_socket_turn_sendto,
+ nr_socket_turn_recvfrom,
+ nr_socket_turn_getfd,
+ nr_socket_turn_getaddr,
+ 0,
+ 0,
+ 0,
+ nr_socket_turn_close,
+ 0,
+ 0
+};
+
+int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp)
+ {
+ int r,_status;
+ nr_socket_turn *sturn=0;
+
+ if(!(sturn=RCALLOC(sizeof(nr_socket_turn))))
+ ABORT(R_NO_MEMORY);
+
+ sturn->magic_cookie = nr_socket_turn_magic_cookie;
+
+ if(r=nr_socket_create_int(sturn, &nr_socket_turn_vtbl, sockp))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if(_status){
+ nr_socket_turn_destroy((void **)&sturn);
+ }
+ return(_status);
+ }
+
+static int nr_socket_turn_destroy(void **objp)
+ {
+ int _status;
+ nr_socket_turn *sturn;
+
+ if(!objp || !*objp)
+ return(0);
+
+ sturn=*objp;
+ *objp=0;
+
+ assert(sturn->magic_cookie == nr_socket_turn_magic_cookie);
+
+ /* we don't own the socket, so don't destroy it */
+
+ RFREE(sturn);
+
+ _status=0;
+ return(_status);
+ }
+
+static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len,
+ int flags, nr_transport_addr *addr)
+ {
+ int r,_status;
+ nr_socket_turn *sturn=obj;
+
+ assert(sturn->magic_cookie == nr_socket_turn_magic_cookie);
+ assert(sturn->turn);
+
+ if ((r = nr_turn_client_send_indication(sturn->turn, msg, len, flags,
+ addr)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_socket_turn_recvfrom(void *obj,void * restrict buf,
+ size_t maxlen, size_t *len, int flags, nr_transport_addr *addr)
+ {
+ /* Reading from TURN sockets is done by the indication
+ processing code in turn_client_ctx. */
+ assert(0);
+
+ return(R_INTERNAL);
+ }
+
+static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd)
+ {
+ /* You should never directly be touching this fd. */
+ assert(0);
+
+ return(R_INTERNAL);
+ }
+
+static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp)
+ {
+ nr_socket_turn *sturn=obj;
+ int r, _status;
+
+ assert(sturn->magic_cookie == nr_socket_turn_magic_cookie);
+ assert(sturn->turn);
+
+ /* This returns the relayed address */
+ if ((r=nr_turn_client_get_relayed_address(sturn->turn, addrp)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_socket_turn_close(void *obj)
+ {
+ /* No-op */
+#ifndef NDEBUG
+ nr_socket_turn *sturn=obj;
+ assert(sturn->magic_cookie == nr_socket_turn_magic_cookie);
+#endif
+
+ return 0;
+ }
+
+int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx)
+{
+ nr_socket_turn *sturn=(nr_socket_turn*)sock->obj;
+ assert(sturn->magic_cookie == nr_socket_turn_magic_cookie);
+ assert(!sturn->turn);
+
+ sturn->turn = ctx;
+
+ return 0;
+}
+
+#endif /* USE_TURN */
diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
new file mode 100644
index 000000000..c9d36d333
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h
@@ -0,0 +1,48 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _nr_socket_turn_h
+#define _nr_socket_turn_h
+
+#include "nr_socket.h"
+
+/* This is a partial implementation of an nr_socket wrapped
+ around TURN. It implements only the nr_socket features
+ actually used by the ICE stack. You can't, for instance,
+ read off the socket */
+int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp);
+int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun.h b/media/mtransport/third_party/nICEr/src/stun/stun.h
new file mode 100644
index 000000000..8e5a60750
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun.h
@@ -0,0 +1,216 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef _STUN_H
+#define _STUN_H
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/param.h>
+#include <sys/socket.h>
+#ifndef LINUX
+#include <net/if.h>
+#if !defined(__OpenBSD__) && !defined(__NetBSD__)
+#include <net/if_var.h>
+#endif
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#else
+#include <linux/if.h>
+#endif
+#ifndef BSD
+#include <net/route.h>
+#endif
+#include <netinet/in.h>
+#ifndef LINUX
+#include <netinet/in_var.h>
+#endif
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include <time.h>
+
+#include "nr_api.h"
+#include "stun_msg.h"
+#include "stun_build.h"
+#include "stun_codec.h"
+#include "stun_hint.h"
+#include "stun_util.h"
+#include "nr_socket.h"
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "stun_proc.h"
+
+#define NR_STUN_VERSION "rfc3489bis-11"
+#define NR_STUN_PORT 3478
+
+/* STUN attributes */
+#define NR_STUN_ATTR_MAPPED_ADDRESS 0x0001
+#define NR_STUN_ATTR_USERNAME 0x0006
+#define NR_STUN_ATTR_MESSAGE_INTEGRITY 0x0008
+#define NR_STUN_ATTR_ERROR_CODE 0x0009
+#define NR_STUN_ATTR_UNKNOWN_ATTRIBUTES 0x000A
+#define NR_STUN_ATTR_REALM 0x0014
+#define NR_STUN_ATTR_NONCE 0x0015
+#define NR_STUN_ATTR_XOR_MAPPED_ADDRESS 0x0020
+#define NR_STUN_ATTR_SERVER 0x8022
+#define NR_STUN_ATTR_ALTERNATE_SERVER 0x8023
+#define NR_STUN_ATTR_FINGERPRINT 0x8028
+
+/* for backwards compatibility with obsolete versions of the STUN spec */
+#define NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS 0x8020
+
+#ifdef USE_STUND_0_96
+#define NR_STUN_ATTR_OLD_CHANGE_REQUEST 0x0003
+#endif /* USE_STUND_0_96 */
+
+#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
+/* for backwards compatibility with obsolete versions of the STUN spec */
+#define NR_STUN_ATTR_OLD_PASSWORD 0x0007
+#define NR_STUN_ATTR_OLD_RESPONSE_ADDRESS 0x0002
+#define NR_STUN_ATTR_OLD_SOURCE_ADDRESS 0x0004
+#define NR_STUN_ATTR_OLD_CHANGED_ADDRESS 0x0005
+#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
+
+#ifdef USE_ICE
+/* ICE attributes */
+#define NR_STUN_ATTR_PRIORITY 0x0024
+#define NR_STUN_ATTR_USE_CANDIDATE 0x0025
+#define NR_STUN_ATTR_ICE_CONTROLLED 0x8029
+#define NR_STUN_ATTR_ICE_CONTROLLING 0x802A
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+/* TURN attributes */
+#define NR_STUN_ATTR_LIFETIME 0x000d
+#define NR_STUN_ATTR_XOR_PEER_ADDRESS 0x0012
+#define NR_STUN_ATTR_DATA 0x0013
+#define NR_STUN_ATTR_XOR_RELAY_ADDRESS 0x0016
+#define NR_STUN_ATTR_REQUESTED_TRANSPORT 0x0019
+
+#define NR_STUN_ATTR_REQUESTED_TRANSPORT_UDP 17
+#endif /* USE_TURN */
+
+/*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |M|M|M|M|M|C|M|M|M|C|M|M|M|M|
+ * |1|1|9|8|7|1|6|5|4|0|3|2|1|0|
+ * |1|0| | | | | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Figure 3: Format of STUN Message Type Field
+ */
+#define NR_STUN_METHOD_TYPE_BITS(m) \
+ ((((m) & 0xf80) << 2) | (((m) & 0x070) << 1) | ((m) & 0x00f))
+
+#define NR_STUN_CLASS_TYPE_BITS(c) \
+ ((((c) & 0x002) << 7) | (((c) & 0x001) << 4))
+
+#define NR_STUN_GET_TYPE_METHOD(t) \
+ ((((t) >> 2) & 0xf80) | (((t) >> 1) & 0x070) | ((t) & 0x00f))
+
+#define NR_STUN_GET_TYPE_CLASS(t) \
+ ((((t) >> 7) & 0x002) | (((t) >> 4) & 0x001))
+
+#define NR_STUN_TYPE(m,c) (NR_STUN_METHOD_TYPE_BITS((m)) | NR_STUN_CLASS_TYPE_BITS((c)))
+
+/* building blocks for message types */
+#define NR_METHOD_BINDING 0x001
+#define NR_CLASS_REQUEST 0x0
+#define NR_CLASS_INDICATION 0x1
+#define NR_CLASS_RESPONSE 0x2
+#define NR_CLASS_ERROR_RESPONSE 0x3
+
+/* define types for STUN messages */
+#define NR_STUN_MSG_BINDING_REQUEST NR_STUN_TYPE(NR_METHOD_BINDING, \
+ NR_CLASS_REQUEST)
+#define NR_STUN_MSG_BINDING_INDICATION NR_STUN_TYPE(NR_METHOD_BINDING, \
+ NR_CLASS_INDICATION)
+#define NR_STUN_MSG_BINDING_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \
+ NR_CLASS_RESPONSE)
+#define NR_STUN_MSG_BINDING_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \
+ NR_CLASS_ERROR_RESPONSE)
+
+#ifdef USE_TURN
+/* building blocks for TURN message types */
+#define NR_METHOD_ALLOCATE 0x003
+#define NR_METHOD_REFRESH 0x004
+
+#define NR_METHOD_SEND 0x006
+#define NR_METHOD_DATA 0x007
+#define NR_METHOD_CREATE_PERMISSION 0x008
+#define NR_METHOD_CHANNEL_BIND 0x009
+
+/* define types for a TURN message */
+#define NR_STUN_MSG_ALLOCATE_REQUEST NR_STUN_TYPE(NR_METHOD_ALLOCATE, \
+ NR_CLASS_REQUEST)
+#define NR_STUN_MSG_ALLOCATE_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \
+ NR_CLASS_RESPONSE)
+#define NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \
+ NR_CLASS_ERROR_RESPONSE)
+#define NR_STUN_MSG_REFRESH_REQUEST NR_STUN_TYPE(NR_METHOD_REFRESH, \
+ NR_CLASS_REQUEST)
+#define NR_STUN_MSG_REFRESH_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \
+ NR_CLASS_RESPONSE)
+#define NR_STUN_MSG_REFRESH_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \
+ NR_CLASS_ERROR_RESPONSE)
+
+#define NR_STUN_MSG_SEND_INDICATION NR_STUN_TYPE(NR_METHOD_SEND, \
+ NR_CLASS_INDICATION)
+#define NR_STUN_MSG_DATA_INDICATION NR_STUN_TYPE(NR_METHOD_DATA, \
+ NR_CLASS_INDICATION)
+
+#define NR_STUN_MSG_PERMISSION_REQUEST NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \
+ NR_CLASS_REQUEST)
+#define NR_STUN_MSG_PERMISSION_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \
+ NR_CLASS_RESPONSE)
+#define NR_STUN_MSG_PERMISSION_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \
+ NR_CLASS_ERROR_RESPONSE)
+
+#define NR_STUN_MSG_CHANNEL_BIND_REQUEST NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \
+ NR_CLASS_REQUEST)
+#define NR_STUN_MSG_CHANNEL_BIND_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \
+ NR_CLASS_RESPONSE)
+#define NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \
+ NR_CLASS_ERROR_RESPONSE)
+
+
+#endif /* USE_TURN */
+
+
+#define NR_STUN_AUTH_RULE_OPTIONAL (1<<0)
+#define NR_STUN_AUTH_RULE_SHORT_TERM (1<<8)
+#define NR_STUN_AUTH_RULE_LONG_TERM (1<<9)
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_build.c b/media/mtransport/third_party/nICEr/src/stun/stun_build.c
new file mode 100644
index 000000000..b029501a7
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_build.c
@@ -0,0 +1,614 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_build.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <csi_platform.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "nr_api.h"
+#include "stun.h"
+#include "registry.h"
+#include "stun_reg.h"
+#include "nr_crypto.h"
+
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 7.1 */
+/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.1 */
+/* note that S 10.1.1 states the message MUST include MESSAGE-INTEGRITY
+ * and USERNAME, but that's not correct -- for instance ICE keepalive
+ * messages don't include these (See draft-ietf-mmusic-ice-18.txt S 10:
+ * "If STUN is being used for keepalives, a STUN Binding Indication is
+ * used. The Indication MUST NOT utilize any authentication mechanism")
+ */
+int
+nr_stun_form_request_or_indication(int mode, int msg_type, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ assert(NR_STUN_GET_TYPE_CLASS(msg_type) == NR_CLASS_REQUEST
+ || NR_STUN_GET_TYPE_CLASS(msg_type) == NR_CLASS_INDICATION);
+
+ *msg = 0;
+
+ if ((r=nr_stun_message_create(&req)))
+ ABORT(r);
+
+ req->header.type = msg_type;
+
+ nr_crypto_random_bytes((UCHAR*)&req->header.id,sizeof(req->header.id));
+
+ switch (mode) {
+ default:
+ if ((r=nr_stun_message_add_fingerprint_attribute(req)))
+ ABORT(r);
+ /* fall through */
+ case NR_STUN_MODE_STUN_NO_AUTH:
+ req->header.magic_cookie = NR_STUN_MAGIC_COOKIE;
+ break;
+
+#ifdef USE_STUND_0_96
+ case NR_STUN_MODE_STUND_0_96:
+ req->header.magic_cookie = NR_STUN_MAGIC_COOKIE2;
+
+ /* actually, stund 0.96 just ignores the fingerprint
+ * attribute, but don't bother to send it */
+
+ break;
+#endif /* USE_STUND_0_96 */
+
+ }
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) RFREE(req);
+ return _status;
+}
+
+int
+nr_stun_build_req_lt_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_username_attribute(req, params->username)))
+ ABORT(r);
+
+ if (params->realm && params->nonce) {
+ if ((r=nr_stun_message_add_realm_attribute(req, params->realm)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_nonce_attribute(req, params->nonce)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_message_integrity_attribute(req, params->password)))
+ ABORT(r);
+ }
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+
+int
+nr_stun_build_req_st_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_username_attribute(req, params->username)))
+ ABORT(r);
+
+ if (params->password) {
+ if ((r=nr_stun_message_add_message_integrity_attribute(req, params->password)))
+ ABORT(r);
+ }
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+
+int
+nr_stun_build_req_no_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN_NO_AUTH, NR_STUN_MSG_BINDING_REQUEST, &req)))
+ ABORT(r);
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+
+int
+nr_stun_build_keepalive(nr_stun_client_stun_keepalive_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *ind = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_INDICATION, &ind)))
+ ABORT(r);
+
+ *msg = ind;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&ind);
+ return _status;
+}
+
+#ifdef USE_STUND_0_96
+int
+nr_stun_build_req_stund_0_96(nr_stun_client_stun_binding_request_stund_0_96_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUND_0_96, NR_STUN_MSG_BINDING_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_change_request_attribute(req, 0)))
+ ABORT(r);
+
+ assert(! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0));
+ assert(! nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0));
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+#endif /* USE_STUND_0_96 */
+
+#ifdef USE_ICE
+int
+nr_stun_build_use_candidate(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_username_attribute(req, params->username)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_message_integrity_attribute(req, &params->password)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_use_candidate_attribute(req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_priority_attribute(req, params->priority)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_ice_controlling_attribute(req, params->tiebreaker)))
+ ABORT(r);
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+
+int
+nr_stun_build_req_ice(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_username_attribute(req, params->username)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_message_integrity_attribute(req, &params->password)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_priority_attribute(req, params->priority)))
+ ABORT(r);
+
+ switch (params->control) {
+ case NR_ICE_CONTROLLING:
+ if ((r=nr_stun_message_add_ice_controlling_attribute(req, params->tiebreaker)))
+ ABORT(r);
+ break;
+ case NR_ICE_CONTROLLED:
+ if ((r=nr_stun_message_add_ice_controlled_attribute(req, params->tiebreaker)))
+ ABORT(r);
+ break;
+ default:
+ assert(0);
+ ABORT(R_INTERNAL);
+ }
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+
+#ifndef __isascii
+#define __isascii(c) (((c) & ~0x7F) == 0)
+#endif
+
+/* Long-term passwords are computed over the key:
+
+ key = MD5(username ":" realm ":" SASLprep(password))
+
+ Per RFC 5389 S 15.4
+*/
+int
+nr_stun_compute_lt_message_integrity_password(const char *username, const char *realm,
+ Data *password, Data *hmac_key)
+{
+ char digest_input[1000];
+ int i;
+ int r, _status;
+ size_t len;
+
+ /* First check that the password is ASCII. We are supposed to
+ SASLprep but we don't support this yet
+ TODO(ekr@rtfm.com): Add SASLprep for password.
+ */
+ for (i=0; i<password->len; i++) {
+ if (!__isascii(password->data[i]))
+ ABORT(R_BAD_DATA);
+ }
+
+ if (hmac_key->len < 16)
+ ABORT(R_BAD_ARGS);
+
+ snprintf(digest_input, sizeof(digest_input), "%s:%s:", username, realm);
+ if ((sizeof(digest_input) - strlen(digest_input)) < password->len)
+ ABORT(R_BAD_DATA);
+
+ len = strlen(digest_input);
+ memcpy(digest_input + len, password->data, password->len);
+
+
+ if (r=nr_crypto_md5((UCHAR *)digest_input, len + password->len, hmac_key->data))
+ ABORT(r);
+ hmac_key->len=16;
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static int
+nr_stun_build_auth_params(nr_stun_client_auth_params *auth, nr_stun_message *req)
+{
+ int r, _status;
+ UCHAR hmac_key_d[16];
+ Data hmac_key;
+
+ ATTACH_DATA(hmac_key, hmac_key_d);
+
+ if (!auth->authenticate)
+ goto done;
+
+ assert(auth->username);
+ assert(auth->password.len);
+ assert(auth->realm);
+ assert(auth->nonce);
+
+ if (r=nr_stun_compute_lt_message_integrity_password(auth->username,
+ auth->realm,
+ &auth->password,
+ &hmac_key))
+ ABORT(r);
+
+ if (!auth->username) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no username provided");
+ ABORT(R_INTERNAL);
+ }
+
+ if (!auth->password.len) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no password provided");
+ ABORT(R_INTERNAL);
+ }
+
+ if (!auth->realm) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no realm provided");
+ ABORT(R_INTERNAL);
+ }
+
+ if (!auth->nonce) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no nonce provided");
+ ABORT(R_INTERNAL);
+ }
+
+ if ((r=nr_stun_message_add_username_attribute(req, auth->username)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_realm_attribute(req, auth->realm)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_nonce_attribute(req, auth->nonce)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_message_integrity_attribute(req, &hmac_key)))
+ ABORT(r);
+
+done:
+ _status=0;
+abort:
+ return(_status);
+}
+
+int
+nr_stun_build_allocate_request(nr_stun_client_auth_params *auth, nr_stun_client_allocate_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_ALLOCATE_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_requested_transport_attribute(req, NR_STUN_ATTR_REQUESTED_TRANSPORT_UDP)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_lifetime_attribute(req, params->lifetime_secs)))
+ ABORT(r);
+
+ /* TODO(ekr@rtfm.com): Add the SOFTWARE attribute (Firefox bug 857666) */
+
+ if ((r=nr_stun_build_auth_params(auth, req)))
+ ABORT(r);
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+
+
+int nr_stun_build_refresh_request(nr_stun_client_auth_params *auth, nr_stun_client_refresh_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_REFRESH_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_lifetime_attribute(req, params->lifetime_secs)))
+ ABORT(r);
+
+
+ /* TODO(ekr@rtfm.com): Add the SOFTWARE attribute (Firefox bug 857666) */
+
+ if ((r=nr_stun_build_auth_params(auth, req)))
+ ABORT(r);
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+
+
+int nr_stun_build_permission_request(nr_stun_client_auth_params *auth, nr_stun_client_permission_request_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *req = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_PERMISSION_REQUEST, &req)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_xor_peer_address_attribute(req, &params->remote_addr)))
+ ABORT(r);
+
+ if ((r=nr_stun_build_auth_params(auth, req)))
+ ABORT(r);
+
+ *msg = req;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&req);
+ return _status;
+}
+
+int
+nr_stun_build_send_indication(nr_stun_client_send_indication_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *ind = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_SEND_INDICATION, &ind)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_xor_peer_address_attribute(ind, &params->remote_addr)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_data_attribute(ind, params->data.data, params->data.len)))
+ ABORT(r);
+
+ *msg = ind;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&ind);
+ return _status;
+}
+
+int
+nr_stun_build_data_indication(nr_stun_client_data_indication_params *params, nr_stun_message **msg)
+{
+ int r,_status;
+ nr_stun_message *ind = 0;
+
+ if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_DATA_INDICATION, &ind)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_xor_peer_address_attribute(ind, &params->remote_addr)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_data_attribute(ind, params->data.data, params->data.len)))
+ ABORT(r);
+
+ *msg = ind;
+
+ _status=0;
+ abort:
+ if (_status) nr_stun_message_destroy(&ind);
+ return _status;
+}
+
+#endif /* USE_TURN */
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1.1 */
+int
+nr_stun_form_success_response(nr_stun_message *req, nr_transport_addr *from, Data *password, nr_stun_message *res)
+{
+ int r,_status;
+ int request_method;
+ char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */
+
+ /* set up information for default response */
+
+ request_method = NR_STUN_GET_TYPE_METHOD(req->header.type);
+ res->header.type = NR_STUN_TYPE(request_method, NR_CLASS_RESPONSE);
+ res->header.magic_cookie = req->header.magic_cookie;
+ memcpy(&res->header.id, &req->header.id, sizeof(res->header.id));
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Mapped Address = %s", from->as_string);
+
+ if ((r=nr_stun_message_add_xor_mapped_address_attribute(res, from)))
+ ABORT(r);
+
+ if (!NR_reg_get_string(NR_STUN_REG_PREF_SERVER_NAME, server_name, sizeof(server_name))) {
+ if ((r=nr_stun_message_add_server_attribute(res, server_name)))
+ ABORT(r);
+ }
+
+ if (res->header.magic_cookie == NR_STUN_MAGIC_COOKIE) {
+ if (password != 0) {
+ if ((r=nr_stun_message_add_message_integrity_attribute(res, password)))
+ ABORT(r);
+ }
+
+ if ((r=nr_stun_message_add_fingerprint_attribute(res)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1.1 */
+void
+nr_stun_form_error_response(nr_stun_message *req, nr_stun_message* res, int number, char* msg)
+{
+ char *str;
+ int request_method;
+ char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */
+
+ if (number < 300 || number > 699)
+ number = 500;
+
+ r_log(NR_LOG_STUN, LOG_INFO, "Responding with error %d: %s", number, msg);
+
+ request_method = NR_STUN_GET_TYPE_METHOD(req->header.type);
+ res->header.type = NR_STUN_TYPE(request_method, NR_CLASS_ERROR_RESPONSE);
+ res->header.magic_cookie = req->header.magic_cookie;
+ memcpy(&res->header.id, &req->header.id, sizeof(res->header.id));
+
+ /* during development we should never see 500s (hopefully not in deployment either) */
+
+ str = 0;
+ switch (number) {
+ case 300: str = "Try Alternate"; break;
+ case 400: str = "Bad Request"; break;
+ case 401: str = "Unauthorized"; break;
+ case 420: str = "Unknown Attribute"; break;
+ case 438: str = "Stale Nonce"; break;
+#ifdef USE_ICE
+ case 487: str = "Role Conflict"; break;
+#endif
+ case 500: str = "Server Error"; break;
+ }
+ if (str == 0) {
+ str = "Unknown";
+ }
+
+ if (nr_stun_message_add_error_code_attribute(res, number, str)) {
+ assert(0); /* should never happen */
+ }
+
+ if (!NR_reg_get_string(NR_STUN_REG_PREF_SERVER_NAME, server_name, sizeof(server_name))) {
+ nr_stun_message_add_server_attribute(res, server_name);
+ }
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_build.h b/media/mtransport/third_party/nICEr/src/stun/stun_build.h
new file mode 100644
index 000000000..c3f91a87b
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_build.h
@@ -0,0 +1,147 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef _stun_build_h
+#define _stun_build_h
+
+#include "stun.h"
+
+#define NR_STUN_MODE_STUN 1
+#ifdef USE_STUND_0_96
+#define NR_STUN_MODE_STUND_0_96 2 /* backwards compatibility mode */
+#endif /* USE_STUND_0_96 */
+#define NR_STUN_MODE_STUN_NO_AUTH 3
+int nr_stun_form_request_or_indication(int mode, int msg_type, nr_stun_message **msg);
+
+typedef struct nr_stun_client_stun_binding_request_params_ {
+ char *username;
+ Data *password;
+ char *nonce;
+ char *realm;
+} nr_stun_client_stun_binding_request_params;
+
+int nr_stun_build_req_lt_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg);
+int nr_stun_build_req_st_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg);
+int nr_stun_build_req_no_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg);
+
+
+typedef struct nr_stun_client_stun_keepalive_params_ {
+#if defined(WIN32) || defined(__clang__)
+ // VC++ and clang give error and warning respectively if no members
+ int dummy;
+#endif
+} nr_stun_client_stun_keepalive_params;
+
+int nr_stun_build_keepalive(nr_stun_client_stun_keepalive_params *params, nr_stun_message **msg);
+
+
+#ifdef USE_STUND_0_96
+typedef struct nr_stun_client_stun_binding_request_stund_0_96_params_ {
+#ifdef WIN32 // silly VC++ gives error if no members
+ int dummy;
+#endif
+} nr_stun_client_stun_binding_request_stund_0_96_params;
+
+int nr_stun_build_req_stund_0_96(nr_stun_client_stun_binding_request_stund_0_96_params *params, nr_stun_message **msg);
+#endif /* USE_STUND_0_96 */
+
+
+#ifdef USE_ICE
+typedef struct nr_stun_client_ice_binding_request_params_ {
+ char *username;
+ Data password;
+ UINT4 priority;
+ int control;
+#define NR_ICE_CONTROLLING 1
+#define NR_ICE_CONTROLLED 2
+ UINT8 tiebreaker;
+} nr_stun_client_ice_binding_request_params;
+
+int nr_stun_build_use_candidate(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg);
+
+int nr_stun_build_req_ice(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg);
+#endif /* USE_ICE */
+
+
+typedef struct nr_stun_client_auth_params_ {
+ char authenticate;
+ char *username;
+ char *realm;
+ char *nonce;
+ Data password;
+} nr_stun_client_auth_params;
+
+#ifdef USE_TURN
+typedef struct nr_stun_client_allocate_request_params_ {
+ UINT4 lifetime_secs;
+} nr_stun_client_allocate_request_params;
+
+int nr_stun_build_allocate_request(nr_stun_client_auth_params *auth, nr_stun_client_allocate_request_params *params, nr_stun_message **msg);
+
+
+typedef struct nr_stun_client_refresh_request_params_ {
+ UINT4 lifetime_secs;
+} nr_stun_client_refresh_request_params;
+
+int nr_stun_build_refresh_request(nr_stun_client_auth_params *auth, nr_stun_client_refresh_request_params *params, nr_stun_message **msg);
+
+
+
+typedef struct nr_stun_client_permission_request_params_ {
+ nr_transport_addr remote_addr;
+} nr_stun_client_permission_request_params;
+
+int nr_stun_build_permission_request(nr_stun_client_auth_params *auth, nr_stun_client_permission_request_params *params, nr_stun_message **msg);
+
+
+typedef struct nr_stun_client_send_indication_params_ {
+ nr_transport_addr remote_addr;
+ Data data;
+} nr_stun_client_send_indication_params;
+
+int nr_stun_build_send_indication(nr_stun_client_send_indication_params *params, nr_stun_message **msg);
+
+typedef struct nr_stun_client_data_indication_params_ {
+ nr_transport_addr remote_addr;
+ Data data;
+} nr_stun_client_data_indication_params;
+
+int nr_stun_build_data_indication(nr_stun_client_data_indication_params *params, nr_stun_message **msg);
+#endif /* USE_TURN */
+
+int nr_stun_form_success_response(nr_stun_message *req, nr_transport_addr *from, Data *password, nr_stun_message *res);
+void nr_stun_form_error_response(nr_stun_message *request, nr_stun_message* response, int number, char* msg);
+int nr_stun_compute_lt_message_integrity_password(const char *username, const char *realm,
+ Data *password, Data *hmac_key);
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
new file mode 100644
index 000000000..e8d91982b
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
@@ -0,0 +1,824 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_client_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+#include <nr_api.h>
+#include "stun.h"
+#include "async_timer.h"
+#include "registry.h"
+#include "stun_reg.h"
+#include "nr_crypto.h"
+#include "r_time.h"
+
+static int nr_stun_client_send_request(nr_stun_client_ctx *ctx);
+static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg);
+static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password);
+
+#define NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD 1
+#define NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK 2
+
+int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp)
+ {
+ nr_stun_client_ctx *ctx=0;
+ char allow_loopback;
+ int r,_status;
+
+ if ((r=nr_stun_startup()))
+ ABORT(r);
+
+ if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ ctx->state=NR_STUN_CLIENT_STATE_INITTED;
+
+ if(!(ctx->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+
+ ctx->sock=sock;
+
+ nr_socket_getaddr(sock,&ctx->my_addr);
+ nr_transport_addr_copy(&ctx->peer_addr,peer);
+
+ if (RTO != 0) {
+ ctx->rto_ms = RTO;
+ } else if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) {
+ ctx->rto_ms = 100;
+ }
+
+ if (NR_reg_get_double(NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF, &ctx->retransmission_backoff_factor))
+ ctx->retransmission_backoff_factor = 2.0;
+
+ if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS, &ctx->maximum_transmits))
+ ctx->maximum_transmits = 7;
+
+ if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->maximum_transmits_timeout_ms))
+ ctx->maximum_transmits_timeout_ms = 16 * ctx->rto_ms;
+
+ ctx->mapped_addr_check_mask = NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD;
+ if (NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback) ||
+ !allow_loopback) {
+ ctx->mapped_addr_check_mask |= NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK;
+ }
+
+ if (ctx->my_addr.protocol == IPPROTO_TCP) {
+ /* Because TCP is reliable there is only one final timeout value.
+ * We store the timeout value for TCP in here, because timeout_ms gets
+ * reset to 0 in client_reset() which gets called from client_start() */
+ ctx->maximum_transmits_timeout_ms = ctx->rto_ms *
+ pow(ctx->retransmission_backoff_factor,
+ ctx->maximum_transmits);
+ ctx->maximum_transmits = 1;
+ }
+
+ *ctxp=ctx;
+
+ _status=0;
+ abort:
+ if(_status){
+ nr_stun_client_ctx_destroy(&ctx);
+ }
+ return(_status);
+ }
+
+static void nr_stun_client_fire_finished_cb(nr_stun_client_ctx *ctx)
+ {
+ if (ctx->finished_cb) {
+ NR_async_cb finished_cb = ctx->finished_cb;
+ ctx->finished_cb = 0; /* prevent 2nd call */
+ /* finished_cb call must be absolutely last thing in function
+ * because as a side effect this ctx may be operated on in the
+ * callback */
+ finished_cb(0,0,ctx->cb_arg);
+ }
+ }
+
+int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg)
+ {
+ int r,_status;
+
+ if (ctx->state != NR_STUN_CLIENT_STATE_INITTED)
+ ABORT(R_NOT_PERMITTED);
+
+ ctx->mode=mode;
+
+ ctx->state=NR_STUN_CLIENT_STATE_RUNNING;
+ ctx->finished_cb=finished_cb;
+ ctx->cb_arg=cb_arg;
+
+ if(mode!=NR_STUN_CLIENT_MODE_KEEPALIVE){
+ if(r=nr_stun_client_send_request(ctx))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) {
+ nr_stun_client_fire_finished_cb(ctx);
+ }
+
+ return(_status);
+ }
+
+int nr_stun_client_restart(nr_stun_client_ctx *ctx)
+ {
+ int r,_status;
+ int mode;
+ NR_async_cb finished_cb;
+ void *cb_arg;
+ nr_stun_message_attribute *ec;
+ nr_stun_message_attribute *as;
+
+ if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
+ ABORT(R_NOT_PERMITTED);
+
+ assert(ctx->retry_ct <= 2);
+ if (ctx->retry_ct > 2)
+ ABORT(R_NOT_PERMITTED);
+
+ ++ctx->retry_ct;
+
+ mode = ctx->mode;
+ finished_cb = ctx->finished_cb;
+ cb_arg = ctx->cb_arg;
+
+ if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_ERROR_CODE, &ec)
+ && ec->u.error_code.number == 300) {
+ if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_ALTERNATE_SERVER, &as)) {
+ nr_transport_addr_copy(&ctx->peer_addr, &as->u.alternate_server);
+ }
+ }
+
+ nr_stun_client_reset(ctx);
+
+ if (r=nr_stun_client_start(ctx, mode, finished_cb, cb_arg))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int
+nr_stun_client_reset(nr_stun_client_ctx *ctx)
+{
+ /* Cancel the timer firing */
+ if (ctx->timer_handle){
+ NR_async_timer_cancel(ctx->timer_handle);
+ ctx->timer_handle=0;
+ }
+
+ nr_stun_message_destroy(&ctx->request);
+ ctx->request = 0;
+
+ nr_stun_message_destroy(&ctx->response);
+ ctx->response = 0;
+
+ memset(&ctx->results, 0, sizeof(ctx->results));
+
+ ctx->mode = 0;
+ ctx->finished_cb = 0;
+ ctx->cb_arg = 0;
+ ctx->request_ct = 0;
+ ctx->timeout_ms = 0;
+
+ ctx->state = NR_STUN_CLIENT_STATE_INITTED;
+
+ return 0;
+}
+
+static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg)
+ {
+ int _status;
+ nr_stun_client_ctx *ctx=cb_arg;
+ struct timeval now;
+ INT8 ms_waited;
+
+ /* Prevent this timer from being cancelled later */
+ ctx->timer_handle=0;
+
+ /* Shouldn't happen */
+ if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED)
+ ABORT(R_REJECTED);
+
+ gettimeofday(&now, 0);
+ if (r_timeval_diff_ms(&now, &ctx->timer_set, &ms_waited)) {
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired",ctx->label);
+ }
+ else {
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired (after %llu ms)",ctx->label, ms_waited);
+ }
+
+ if (ctx->request_ct >= ctx->maximum_transmits) {
+ r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Timed out",ctx->label);
+ ctx->state=NR_STUN_CLIENT_STATE_TIMED_OUT;
+ ABORT(R_FAILED);
+ }
+
+ if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
+ ABORT(R_NOT_PERMITTED);
+
+ /* as a side effect will reset the timer */
+ nr_stun_client_send_request(ctx);
+
+ _status = 0;
+ abort:
+ if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) {
+ /* Cancel the timer firing */
+ if (ctx->timer_handle){
+ NR_async_timer_cancel(ctx->timer_handle);
+ ctx->timer_handle=0;
+ }
+
+ nr_stun_client_fire_finished_cb(ctx);
+ }
+ return;
+ }
+
+int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx)
+ {
+ int r,_status;
+
+ if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
+ ABORT(R_NOT_PERMITTED);
+
+ if (ctx->request_ct > ctx->maximum_transmits) {
+ r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Too many retransmit attempts",ctx->label);
+ ABORT(R_FAILED);
+ }
+
+ /* if there is a scheduled retransimt, get rid of the scheduled retransmit
+ * and retransmit immediately */
+ if (ctx->timer_handle) {
+ NR_async_timer_cancel(ctx->timer_handle);
+ ctx->timer_handle=0;
+
+ if (r=nr_stun_client_send_request(ctx))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+
+ return(_status);
+ }
+
+static int nr_stun_client_send_request(nr_stun_client_ctx *ctx)
+ {
+ int r,_status;
+ char string[256];
+
+ if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
+ ABORT(R_NOT_PERMITTED);
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Sending check request (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,ctx->peer_addr.as_string);
+
+ if (ctx->request == 0) {
+ switch (ctx->mode) {
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH:
+ ctx->params.stun_binding_request.nonce = ctx->nonce;
+ ctx->params.stun_binding_request.realm = ctx->realm;
+ assert(0);
+ ABORT(R_INTERNAL);
+ /* TODO(ekr@rtfm.com): Need to implement long-term auth for binding
+ requests */
+ if ((r=nr_stun_build_req_lt_auth(&ctx->params.stun_binding_request, &ctx->request)))
+ ABORT(r);
+ break;
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH:
+ if ((r=nr_stun_build_req_st_auth(&ctx->params.stun_binding_request, &ctx->request)))
+ ABORT(r);
+ break;
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH:
+ if ((r=nr_stun_build_req_no_auth(&ctx->params.stun_binding_request, &ctx->request)))
+ ABORT(r);
+ break;
+ case NR_STUN_CLIENT_MODE_KEEPALIVE:
+ if ((r=nr_stun_build_keepalive(&ctx->params.stun_keepalive, &ctx->request)))
+ ABORT(r);
+ break;
+#ifdef USE_STUND_0_96
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96:
+ if ((r=nr_stun_build_req_stund_0_96(&ctx->params.stun_binding_request_stund_0_96, &ctx->request)))
+ ABORT(r);
+ break;
+#endif /* USE_STUND_0_96 */
+
+#ifdef USE_ICE
+ case NR_ICE_CLIENT_MODE_USE_CANDIDATE:
+ if ((r=nr_stun_build_use_candidate(&ctx->params.ice_binding_request, &ctx->request)))
+ ABORT(r);
+ break;
+ case NR_ICE_CLIENT_MODE_BINDING_REQUEST:
+ if ((r=nr_stun_build_req_ice(&ctx->params.ice_binding_request, &ctx->request)))
+ ABORT(r);
+ break;
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+ case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST:
+ if ((r=nr_stun_build_allocate_request(&ctx->auth_params, &ctx->params.allocate_request, &ctx->request)))
+ ABORT(r);
+ break;
+ case NR_TURN_CLIENT_MODE_REFRESH_REQUEST:
+ if ((r=nr_stun_build_refresh_request(&ctx->auth_params, &ctx->params.refresh_request, &ctx->request)))
+ ABORT(r);
+ break;
+ case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST:
+ if ((r=nr_stun_build_permission_request(&ctx->auth_params, &ctx->params.permission_request, &ctx->request)))
+ ABORT(r);
+ break;
+ case NR_TURN_CLIENT_MODE_SEND_INDICATION:
+ if ((r=nr_stun_build_send_indication(&ctx->params.send_indication, &ctx->request)))
+ ABORT(r);
+ break;
+#endif /* USE_TURN */
+
+ default:
+ assert(0);
+ ABORT(R_FAILED);
+ break;
+ }
+ }
+
+ if (ctx->request->length == 0) {
+ if ((r=nr_stun_encode_message(ctx->request)))
+ ABORT(r);
+ }
+
+ snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Sending to %s ", ctx->label, ctx->peer_addr.as_string);
+ r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)ctx->request->buffer, ctx->request->length);
+
+ assert(ctx->my_addr.protocol==ctx->peer_addr.protocol);
+
+ if(r=nr_socket_sendto(ctx->sock, ctx->request->buffer, ctx->request->length, 0, &ctx->peer_addr)) {
+ if (r != R_WOULDBLOCK) {
+ ABORT(r);
+ }
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_socket_sendto blocked, treating as dropped packet",ctx->label);
+ }
+
+ ctx->request_ct++;
+
+ if (NR_STUN_GET_TYPE_CLASS(ctx->request->header.type) == NR_CLASS_INDICATION) {
+ /* no need to set the timer because indications don't receive a
+ * response */
+ }
+ else {
+ if (ctx->request_ct >= ctx->maximum_transmits) {
+ /* Reliable transport only get here once. Unreliable get here for
+ * their final timeout. */
+ ctx->timeout_ms += ctx->maximum_transmits_timeout_ms;
+ }
+ else if (ctx->timeout_ms) {
+ /* exponential backoff */
+ ctx->timeout_ms *= ctx->retransmission_backoff_factor;
+ }
+ else {
+ /* initial timeout unreliable transports */
+ ctx->timeout_ms = ctx->rto_ms;
+ }
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Next timer will fire in %u ms",ctx->label, ctx->timeout_ms);
+
+ gettimeofday(&ctx->timer_set, 0);
+
+ assert(ctx->timeout_ms);
+ NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle);
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ nr_stun_client_failed(ctx);
+ }
+ return(_status);
+ }
+
+static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password)
+{
+ *password = (Data*)arg;
+ if (!arg)
+ return(R_NOT_FOUND);
+ return(0);
+}
+
+int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 check)
+ {
+ if((check & NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD) && nr_transport_addr_is_wildcard(addr))
+ return(R_BAD_DATA);
+
+ if ((check & NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK) && nr_transport_addr_is_loopback(addr))
+ return(R_BAD_DATA);
+
+ return(0);
+ }
+
+int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr)
+ {
+ int r,_status;
+ char string[256];
+ char *username = 0;
+ Data *password = 0;
+ nr_stun_message_attribute *attr;
+ nr_transport_addr *mapped_addr = 0;
+ int fail_on_error = 0;
+ UCHAR hmac_key_d[16];
+ Data hmac_key;
+ int compute_lt_key=0;
+ /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */
+ int response_matched=0;
+
+ ATTACH_DATA(hmac_key, hmac_key_d);
+
+ if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) &&
+ (ctx->state != NR_STUN_CLIENT_STATE_WAITING))
+ ABORT(R_REJECTED);
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Inspecting STUN response (my_addr=%s, peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
+
+ snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Received ", ctx->label);
+ r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len);
+
+ /* determine password */
+ switch (ctx->mode) {
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH:
+ compute_lt_key = 1;
+ /* Fall through */
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH:
+ password = ctx->params.stun_binding_request.password;
+ break;
+
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH:
+ /* do nothing */
+ break;
+
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96:
+ /* do nothing */
+ break;
+
+#ifdef USE_ICE
+ case NR_ICE_CLIENT_MODE_BINDING_REQUEST:
+ password = &ctx->params.ice_binding_request.password;
+ break;
+ case NR_ICE_CLIENT_MODE_USE_CANDIDATE:
+ password = &ctx->params.ice_binding_request.password;
+ break;
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+ case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST:
+ fail_on_error = 1;
+ compute_lt_key = 1;
+ username = ctx->auth_params.username;
+ password = &ctx->auth_params.password;
+ /* do nothing */
+ break;
+ case NR_TURN_CLIENT_MODE_REFRESH_REQUEST:
+ fail_on_error = 1;
+ compute_lt_key = 1;
+ username = ctx->auth_params.username;
+ password = &ctx->auth_params.password;
+ /* do nothing */
+ break;
+ case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST:
+ fail_on_error = 1;
+ compute_lt_key = 1;
+ username = ctx->auth_params.username;
+ password = &ctx->auth_params.password;
+ /* do nothing */
+ break;
+ case NR_TURN_CLIENT_MODE_SEND_INDICATION:
+ /* do nothing -- we just got our DATA-INDICATION */
+ break;
+#endif /* USE_TURN */
+
+ default:
+ assert(0);
+ ABORT(R_FAILED);
+ break;
+ }
+
+ if (compute_lt_key) {
+ if (!ctx->realm || !username) {
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Long-term auth required but no realm/username specified. Randomizing key");
+ /* Fill the key with random bytes to guarantee non-match */
+ if (r=nr_crypto_random_bytes(hmac_key_d, sizeof(hmac_key_d)))
+ ABORT(r);
+ }
+ else {
+ if (r=nr_stun_compute_lt_message_integrity_password(username, ctx->realm,
+ password, &hmac_key))
+ ABORT(r);
+ }
+ password = &hmac_key;
+ }
+
+ if (ctx->response) {
+ nr_stun_message_destroy(&ctx->response);
+ }
+
+ /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */
+ if ((r=nr_stun_message_create2(&ctx->response, msg, len)))
+ ABORT(r);
+
+ if ((r=nr_stun_decode_message(ctx->response, nr_stun_client_get_password, password))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): error decoding response",ctx->label);
+ ABORT(r);
+ }
+
+ /* This will return an error if request and response don't match,
+ which is how we reject responses that match other contexts. */
+ if ((r=nr_stun_receive_message(ctx->request, ctx->response))) {
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Response is not for us",ctx->label);
+ ABORT(r);
+ }
+
+ r_log(NR_LOG_STUN,LOG_INFO,
+ "STUN-CLIENT(%s): Received response; processing",ctx->label);
+ response_matched=1;
+
+/* TODO: !nn! currently using password!=0 to mean that auth is required,
+ * TODO: !nn! but we should probably pass that in explicitly via the
+ * TODO: !nn! usage (ctx->mode?) */
+ if (password) {
+ if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_NONCE, 0)) {
+ if ((r=nr_stun_receive_response_long_term_auth(ctx->response, ctx)))
+ ABORT(r);
+ }
+ else {
+ if ((r=nr_stun_receive_response_short_term_auth(ctx->response)))
+ ABORT(r);
+ }
+ }
+
+ if (NR_STUN_GET_TYPE_CLASS(ctx->response->header.type) == NR_CLASS_RESPONSE) {
+ if ((r=nr_stun_process_success_response(ctx->response)))
+ ABORT(r);
+ }
+ else {
+ if (fail_on_error) {
+ ctx->state = NR_STUN_CLIENT_STATE_FAILED;
+ }
+ /* Note: most times we call process_error_response, we get r != 0.
+
+ However, if the error is to be discarded, we get r == 0, smash
+ the error code, and just keep going.
+ */
+ if ((r=nr_stun_process_error_response(ctx->response, &ctx->error_code))) {
+ ABORT(r);
+ }
+ else {
+ ctx->error_code = 0xffff;
+ /* drop the error on the floor */
+ ABORT(R_FAILED);
+ }
+ }
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Successfully parsed mode=%d",ctx->label,ctx->mode);
+
+/* TODO: !nn! this should be moved to individual message receive/processing sections */
+ switch (ctx->mode) {
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH:
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0))
+ ABORT(R_BAD_DATA);
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ ABORT(R_BAD_DATA);
+
+ mapped_addr = &ctx->results.stun_binding_response.mapped_addr;
+ break;
+
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) {
+ if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) {
+ /* Compensate for a bug in Google's STUN servers where they always respond with MAPPED-ADDRESS */
+ r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS but MAPPED-ADDRESS. Falling back (though server is wrong).", ctx->label);
+ }
+ else {
+ ABORT(R_BAD_DATA);
+ }
+ }
+
+ mapped_addr = &ctx->results.stun_binding_response.mapped_addr;
+ break;
+
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0) && ! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0))
+ ABORT(R_BAD_DATA);
+
+ mapped_addr = &ctx->results.stun_binding_response_stund_0_96.mapped_addr;
+ break;
+
+#ifdef USE_ICE
+ case NR_ICE_CLIENT_MODE_BINDING_REQUEST:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0))
+ ABORT(R_BAD_DATA);
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ ABORT(R_BAD_DATA);
+
+ mapped_addr = &ctx->results.stun_binding_response.mapped_addr;
+ break;
+ case NR_ICE_CLIENT_MODE_USE_CANDIDATE:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0))
+ ABORT(R_BAD_DATA);
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ ABORT(R_BAD_DATA);
+
+ mapped_addr = &ctx->results.stun_binding_response.mapped_addr;
+ break;
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+ case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0))
+ ABORT(R_BAD_DATA);
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ ABORT(R_BAD_DATA);
+
+ if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_RELAY_ADDRESS, &attr))
+ ABORT(R_BAD_DATA);
+
+ if ((r=nr_stun_transport_addr_check(&attr->u.relay_address.unmasked,
+ ctx->mapped_addr_check_mask)))
+ ABORT(r);
+
+ if ((r=nr_transport_addr_copy(
+ &ctx->results.allocate_response.relay_addr,
+ &attr->u.relay_address.unmasked)))
+ ABORT(r);
+
+ if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr))
+ ABORT(R_BAD_DATA);
+ ctx->results.allocate_response.lifetime_secs=attr->u.lifetime_secs;
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received relay address: %s", ctx->label, ctx->results.allocate_response.relay_addr.as_string);
+
+ mapped_addr = &ctx->results.allocate_response.mapped_addr;
+
+ break;
+ case NR_TURN_CLIENT_MODE_REFRESH_REQUEST:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ ABORT(R_BAD_DATA);
+ if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr))
+ ABORT(R_BAD_DATA);
+ ctx->results.refresh_response.lifetime_secs=attr->u.lifetime_secs;
+ break;
+ case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST:
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0))
+ ABORT(R_BAD_DATA);
+ break;
+#endif /* USE_TURN */
+
+ default:
+ assert(0);
+ ABORT(R_FAILED);
+ break;
+ }
+
+ /* make sure we have the most up-to-date address from this peer */
+ if (nr_transport_addr_cmp(&ctx->peer_addr, peer_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Peer moved from %s to %s", ctx->label, ctx->peer_addr.as_string, peer_addr->as_string);
+ nr_transport_addr_copy(&ctx->peer_addr, peer_addr);
+ }
+
+ if (mapped_addr) {
+ if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, &attr)) {
+ if ((r=nr_stun_transport_addr_check(&attr->u.xor_mapped_address.unmasked,
+ ctx->mapped_addr_check_mask)))
+ ABORT(r);
+
+ if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.xor_mapped_address.unmasked)))
+ ABORT(r);
+ }
+ else if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, &attr)) {
+ if ((r=nr_stun_transport_addr_check(&attr->u.mapped_address,
+ ctx->mapped_addr_check_mask)))
+ ABORT(r);
+
+ if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.mapped_address)))
+ ABORT(r);
+ }
+ else
+ ABORT(R_BAD_DATA);
+
+ // STUN doesn't distinguish protocol in mapped address, therefore
+ // assign used protocol from peer_addr
+ if (mapped_addr->protocol!=peer_addr->protocol){
+ mapped_addr->protocol=peer_addr->protocol;
+ nr_transport_addr_fmt_addr_string(mapped_addr);
+ }
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received mapped address: %s", ctx->label, mapped_addr->as_string);
+ }
+
+ ctx->state=NR_STUN_CLIENT_STATE_DONE;
+
+ _status=0;
+ abort:
+ if(_status && response_matched){
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): Error processing response: %s, stun error code %d.", ctx->label, nr_strerror(_status), (int)ctx->error_code);
+ }
+
+ if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) &&
+ (ctx->state != NR_STUN_CLIENT_STATE_WAITING)) {
+ /* Cancel the timer firing */
+ if (ctx->timer_handle) {
+ NR_async_timer_cancel(ctx->timer_handle);
+ ctx->timer_handle = 0;
+ }
+
+ nr_stun_client_fire_finished_cb(ctx);
+ }
+
+ return(_status);
+ }
+
+int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp)
+ {
+ nr_stun_client_ctx *ctx;
+
+ if(!ctxp || !*ctxp)
+ return(0);
+
+ ctx=*ctxp;
+ *ctxp=0;
+
+ nr_stun_client_reset(ctx);
+
+ RFREE(ctx->nonce);
+ RFREE(ctx->realm);
+
+ RFREE(ctx->label);
+ RFREE(ctx);
+
+ return(0);
+ }
+
+
+int nr_stun_client_cancel(nr_stun_client_ctx *ctx)
+ {
+ /* Cancel the timer firing */
+ if (ctx->timer_handle){
+ NR_async_timer_cancel(ctx->timer_handle);
+ ctx->timer_handle=0;
+ }
+
+ /* Mark cancelled so we ignore any returned messsages */
+ ctx->state=NR_STUN_CLIENT_STATE_CANCELLED;
+ return(0);
+}
+
+int nr_stun_client_wait(nr_stun_client_ctx *ctx)
+ {
+ nr_stun_client_cancel(ctx);
+ ctx->state=NR_STUN_CLIENT_STATE_WAITING;
+
+ ctx->request_ct = ctx->maximum_transmits;
+ ctx->timeout_ms = ctx->maximum_transmits_timeout_ms;
+ NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle);
+
+ return(0);
+ }
+
+int nr_stun_client_failed(nr_stun_client_ctx *ctx)
+ {
+ nr_stun_client_cancel(ctx);
+ ctx->state=NR_STUN_CLIENT_STATE_FAILED;
+ nr_stun_client_fire_finished_cb(ctx);
+ return(0);
+ }
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h
new file mode 100644
index 000000000..179f16cba
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h
@@ -0,0 +1,199 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _stun_client_ctx_h
+#define _stun_client_ctx_h
+
+/* forward declaration */
+typedef struct nr_stun_client_ctx_ nr_stun_client_ctx;
+
+#include "stun.h"
+
+/* Checklist for adding new STUN transaction types
+
+ 1. Add new method type in stun.h (NR_METHOD_*)
+ 2. Add new MSGs in stun.h (NR_STUN_MSG_*)
+ 3. Add new messages to stun_util.c:nr_stun_msg_type
+ 4. Add new request type to stun_build.h
+ 4. Add new message builder to stun_build.c
+ 5. Add new response type to stun_client_ctx.h
+ 6. Add new arm to stun_client_ctx.c:nr_stun_client_send_request
+ 7. Add new arms to nr_stun_client_process_response
+ 8. Add new arms to stun_hint.c:nr_is_stun_message
+*/
+
+
+
+
+typedef union nr_stun_client_params_ {
+
+ nr_stun_client_stun_binding_request_params stun_binding_request;
+ nr_stun_client_stun_keepalive_params stun_keepalive;
+#ifdef USE_STUND_0_96
+ nr_stun_client_stun_binding_request_stund_0_96_params stun_binding_request_stund_0_96;
+#endif /* USE_STUND_0_96 */
+
+#ifdef USE_ICE
+ nr_stun_client_ice_binding_request_params ice_binding_request;
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+ nr_stun_client_allocate_request_params allocate_request;
+ nr_stun_client_refresh_request_params refresh_request;
+ nr_stun_client_permission_request_params permission_request;
+ nr_stun_client_send_indication_params send_indication;
+#endif /* USE_TURN */
+
+} nr_stun_client_params;
+
+typedef struct nr_stun_client_stun_binding_response_results_ {
+ nr_transport_addr mapped_addr;
+} nr_stun_client_stun_binding_response_results;
+
+typedef struct nr_stun_client_stun_binding_response_stund_0_96_results_ {
+ nr_transport_addr mapped_addr;
+} nr_stun_client_stun_binding_response_stund_0_96_results;
+
+#ifdef USE_ICE
+typedef struct nr_stun_client_ice_use_candidate_results_ {
+#ifdef WIN32 // silly VC++ gives error if no members
+ int dummy;
+#endif
+} nr_stun_client_ice_use_candidate_results;
+
+typedef struct nr_stun_client_ice_binding_response_results_ {
+ nr_transport_addr mapped_addr;
+} nr_stun_client_ice_binding_response_results;
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+typedef struct nr_stun_client_allocate_response_results_ {
+ nr_transport_addr relay_addr;
+ nr_transport_addr mapped_addr;
+ UINT4 lifetime_secs;
+} nr_stun_client_allocate_response_results;
+
+typedef struct nr_stun_client_refresh_response_results_ {
+ UINT4 lifetime_secs;
+} nr_stun_client_refresh_response_results;
+
+typedef struct nr_stun_client_permission_response_results_ {
+ UINT4 lifetime_secs;
+} nr_stun_client_permission_response_results;
+
+#endif /* USE_TURN */
+
+typedef union nr_stun_client_results_ {
+ nr_stun_client_stun_binding_response_results stun_binding_response;
+ nr_stun_client_stun_binding_response_stund_0_96_results stun_binding_response_stund_0_96;
+
+#ifdef USE_ICE
+ nr_stun_client_ice_use_candidate_results ice_use_candidate;
+ nr_stun_client_ice_binding_response_results ice_binding_response;
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+ nr_stun_client_allocate_response_results allocate_response;
+ nr_stun_client_refresh_response_results refresh_response;
+#endif /* USE_TURN */
+} nr_stun_client_results;
+
+struct nr_stun_client_ctx_ {
+ int state;
+#define NR_STUN_CLIENT_STATE_INITTED 0
+#define NR_STUN_CLIENT_STATE_RUNNING 1
+#define NR_STUN_CLIENT_STATE_DONE 2
+#define NR_STUN_CLIENT_STATE_FAILED 3
+#define NR_STUN_CLIENT_STATE_TIMED_OUT 4
+#define NR_STUN_CLIENT_STATE_CANCELLED 5
+#define NR_STUN_CLIENT_STATE_WAITING 6
+
+ int mode;
+#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH 1
+#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH 2
+#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH 3
+#define NR_STUN_CLIENT_MODE_KEEPALIVE 4
+#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96 5
+#ifdef USE_ICE
+#define NR_ICE_CLIENT_MODE_USE_CANDIDATE 10
+#define NR_ICE_CLIENT_MODE_BINDING_REQUEST 11
+#endif /* USE_ICE */
+#ifdef USE_TURN
+#define NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST 20
+#define NR_TURN_CLIENT_MODE_REFRESH_REQUEST 21
+#define NR_TURN_CLIENT_MODE_SEND_INDICATION 22
+#define NR_TURN_CLIENT_MODE_DATA_INDICATION 24
+#define NR_TURN_CLIENT_MODE_PERMISSION_REQUEST 25
+#endif /* USE_TURN */
+
+ char *label;
+ nr_transport_addr my_addr;
+ nr_transport_addr peer_addr;
+ nr_socket *sock;
+ nr_stun_client_auth_params auth_params;
+ nr_stun_client_params params;
+ nr_stun_client_results results;
+ char *nonce;
+ char *realm;
+ void *timer_handle;
+ int request_ct;
+ UINT4 rto_ms; /* retransmission time out */
+ double retransmission_backoff_factor;
+ UINT4 maximum_transmits;
+ UINT4 maximum_transmits_timeout_ms;
+ UINT4 mapped_addr_check_mask; /* What checks to run on mapped addresses */
+ int timeout_ms;
+ struct timeval timer_set;
+ int retry_ct;
+ NR_async_cb finished_cb;
+ void *cb_arg;
+ nr_stun_message *request;
+ nr_stun_message *response;
+ UINT2 error_code;
+};
+
+int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp);
+int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg);
+int nr_stun_client_restart(nr_stun_client_ctx *ctx);
+int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx);
+int nr_stun_client_reset(nr_stun_client_ctx *ctx);
+int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp);
+int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 mask);
+int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr);
+int nr_stun_client_cancel(nr_stun_client_ctx *ctx);
+int nr_stun_client_wait(nr_stun_client_ctx *ctx);
+int nr_stun_client_failed(nr_stun_client_ctx *ctx);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_codec.c b/media/mtransport/third_party/nICEr/src/stun/stun_codec.c
new file mode 100644
index 000000000..6faab70de
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_codec.c
@@ -0,0 +1,1522 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_codec.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <errno.h>
+#include <csi_platform.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <stdlib.h>
+#include <io.h>
+#include <time.h>
+#else /* UNIX */
+#include <string.h>
+#endif /* end UNIX */
+#include <assert.h>
+#include <stddef.h>
+
+#include "nr_api.h"
+#include "stun.h"
+#include "byteorder.h"
+#include "r_crc32.h"
+#include "nr_crypto.h"
+#include "mbslen.h"
+
+#define NR_STUN_IPV4_FAMILY 0x01
+#define NR_STUN_IPV6_FAMILY 0x02
+
+#define SKIP_ATTRIBUTE_DECODE -1
+
+static int nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info);
+
+static int nr_stun_fix_attribute_ordering(nr_stun_message *msg);
+
+static int nr_stun_encode_htons(UINT2 data, int buflen, UCHAR *buf, int *offset);
+static int nr_stun_encode_htonl(UINT4 data, int buflen, UCHAR *buf, int *offset);
+static int nr_stun_encode_htonll(UINT8 data, int buflen, UCHAR *buf, int *offset);
+static int nr_stun_encode(UCHAR *data, int length, int buflen, UCHAR *buf, int *offset);
+
+static int nr_stun_decode_htons(UCHAR *buf, int buflen, int *offset, UINT2 *data);
+static int nr_stun_decode_htonl(UCHAR *buf, int buflen, int *offset, UINT4 *data);
+static int nr_stun_decode_htonll(UCHAR *buf, int buflen, int *offset, UINT8 *data);
+static int nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data);
+
+static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, int len, void *data, int max_bytes, int max_chars);
+
+static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+static int
+nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data);
+
+
+int
+nr_stun_encode_htons(UINT2 data, int buflen, UCHAR *buf, int *offset)
+{
+ UINT2 d = htons(data);
+
+ if (*offset + sizeof(d) >= buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd >= %d", *offset, sizeof(d), buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(&buf[*offset], &d, sizeof(d));
+ *offset += sizeof(d);
+
+ return 0;
+}
+
+int
+nr_stun_encode_htonl(UINT4 data, int buflen, UCHAR *buf, int *offset)
+{
+ UINT4 d = htonl(data);
+
+ if (*offset + sizeof(d) > buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(&buf[*offset], &d, sizeof(d));
+ *offset += sizeof(d);
+
+ return 0;
+}
+
+int
+nr_stun_encode_htonll(UINT8 data, int buflen, UCHAR *buf, int *offset)
+{
+ UINT8 d = nr_htonll(data);
+
+ if (*offset + sizeof(d) > buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(&buf[*offset], &d, sizeof(d));
+ *offset += sizeof(d);
+
+ return 0;
+}
+
+int
+nr_stun_encode(UCHAR *data, int length, int buflen, UCHAR *buf, int *offset)
+{
+ if (*offset + length > buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(&buf[*offset], data, length);
+ *offset += length;
+
+ return 0;
+}
+
+
+int
+nr_stun_decode_htons(UCHAR *buf, int buflen, int *offset, UINT2 *data)
+{
+ UINT2 d;
+
+ if (*offset + sizeof(d) > buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(&d, &buf[*offset], sizeof(d));
+ *offset += sizeof(d);
+ *data = htons(d);
+
+ return 0;
+}
+
+int
+nr_stun_decode_htonl(UCHAR *buf, int buflen, int *offset, UINT4 *data)
+{
+ UINT4 d;
+
+ if (*offset + sizeof(d) > buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(&d, &buf[*offset], sizeof(d));
+ *offset += sizeof(d);
+ *data = htonl(d);
+
+ return 0;
+}
+
+int
+nr_stun_decode_htonll(UCHAR *buf, int buflen, int *offset, UINT8 *data)
+{
+ UINT8 d;
+
+ if (*offset + sizeof(d) > buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(&d, &buf[*offset], sizeof(d));
+ *offset += sizeof(d);
+ *data = nr_htonll(d);
+
+ return 0;
+}
+
+int
+nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data)
+{
+ if (*offset + length > buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen);
+ return R_BAD_DATA;
+ }
+
+ memcpy(data, &buf[*offset], length);
+ *offset += length;
+
+ return 0;
+}
+
+int
+nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, int len, void *data, int max_bytes, int max_chars)
+{
+ int _status;
+ char *s = data;
+ size_t nchars;
+
+ if (len > max_bytes) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %d bytes", attr_info->name, len);
+ ABORT(R_FAILED);
+ }
+
+ if (max_chars >= 0) {
+ if (mbslen(s, &nchars)) {
+ /* who knows what to do, just assume everything is working ok */
+ }
+ else if (nchars > max_chars) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %zd characters", attr_info->name, nchars);
+ ABORT(R_FAILED);
+ }
+ }
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+int
+nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data)
+{
+ int r,_status;
+ nr_stun_attr_error_code *ec = data;
+
+ if (ec->number < 300 || ec->number > 699)
+ ABORT(R_FAILED);
+
+ if ((r=nr_stun_attr_string_illegal(attr_info, strlen(ec->reason), ec->reason, NR_STUN_MAX_ERROR_CODE_REASON_BYTES, NR_STUN_MAX_ERROR_CODE_REASON_CHARS)))
+ ABORT(r);
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+int
+nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data)
+{
+ return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_NONCE_BYTES, NR_STUN_MAX_NONCE_CHARS);
+}
+
+int
+nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data)
+{
+ return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_REALM_BYTES, NR_STUN_MAX_REALM_CHARS);
+}
+
+int
+nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data)
+{
+ return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_SERVER_BYTES, NR_STUN_MAX_SERVER_CHARS);
+}
+
+int
+nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data)
+{
+ return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_USERNAME_BYTES, -1);
+}
+
+static int
+nr_stun_attr_codec_UCHAR_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UCHAR*)data);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_UCHAR_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int start = offset;
+ UINT4 tmp = *((UCHAR *)data);
+ tmp <<= 24;
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset)
+ || nr_stun_encode_htonl(tmp , buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_UCHAR_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ UINT4 tmp;
+
+ if (attrlen != sizeof(UINT4)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen);
+ return R_FAILED;
+ }
+
+ if (nr_stun_decode_htonl(buf, buflen, &offset, &tmp))
+ return R_FAILED;
+
+ *((UCHAR *)data) = (tmp >> 24) & 0xff;
+
+ return 0;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_UCHAR = {
+ "UCHAR",
+ nr_stun_attr_codec_UCHAR_print,
+ nr_stun_attr_codec_UCHAR_encode,
+ nr_stun_attr_codec_UCHAR_decode
+};
+
+static int
+nr_stun_attr_codec_UINT4_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UINT4*)data);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_UINT4_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int start = offset;
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset)
+ || nr_stun_encode_htonl(*(UINT4*)data , buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_UINT4_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ if (attrlen != sizeof(UINT4)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen);
+ return R_FAILED;
+ }
+
+ if (nr_stun_decode_htonl(buf, buflen, &offset, (UINT4*)data))
+ return R_FAILED;
+
+ return 0;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_UINT4 = {
+ "UINT4",
+ nr_stun_attr_codec_UINT4_print,
+ nr_stun_attr_codec_UINT4_encode,
+ nr_stun_attr_codec_UINT4_decode
+};
+
+static int
+nr_stun_attr_codec_UINT8_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %llu", msg, attr_info->name, *(UINT8*)data);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_UINT8_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int start = offset;
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(sizeof(UINT8) , buflen, buf, &offset)
+ || nr_stun_encode_htonll(*(UINT8*)data , buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_UINT8_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ if (attrlen != sizeof(UINT8)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen);
+ return R_FAILED;
+ }
+
+ if (nr_stun_decode_htonll(buf, buflen, &offset, (UINT8*)data))
+ return R_FAILED;
+
+ return 0;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_UINT8 = {
+ "UINT8",
+ nr_stun_attr_codec_UINT8_print,
+ nr_stun_attr_codec_UINT8_encode,
+ nr_stun_attr_codec_UINT8_decode
+};
+
+static int
+nr_stun_attr_codec_addr_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", msg, attr_info->name, ((nr_transport_addr*)data)->as_string);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_addr_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int r,_status;
+ int start = offset;
+ nr_transport_addr *addr = data;
+ UCHAR pad = '\0';
+ UCHAR family;
+
+ if ((r=nr_stun_encode_htons(attr_info->type, buflen, buf, &offset)))
+ ABORT(r);
+
+ switch (addr->ip_version) {
+ case NR_IPV4:
+ family = NR_STUN_IPV4_FAMILY;
+ if (nr_stun_encode_htons(8 , buflen, buf, &offset)
+ || nr_stun_encode(&pad, 1 , buflen, buf, &offset)
+ || nr_stun_encode(&family, 1 , buflen, buf, &offset)
+ || nr_stun_encode_htons(ntohs(addr->u.addr4.sin_port), buflen, buf, &offset)
+ || nr_stun_encode_htonl(ntohl(addr->u.addr4.sin_addr.s_addr), buflen, buf, &offset))
+ ABORT(R_FAILED);
+ break;
+
+ case NR_IPV6:
+ family = NR_STUN_IPV6_FAMILY;
+ if (nr_stun_encode_htons(20 , buflen, buf, &offset)
+ || nr_stun_encode(&pad, 1 , buflen, buf, &offset)
+ || nr_stun_encode(&family, 1 , buflen, buf, &offset)
+ || nr_stun_encode_htons(ntohs(addr->u.addr6.sin6_port), buflen, buf, &offset)
+ || nr_stun_encode(addr->u.addr6.sin6_addr.s6_addr, 16, buflen, buf, &offset))
+ ABORT(R_FAILED);
+ break;
+
+ default:
+ assert(0);
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ *attrlen = offset - start;
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+static int
+nr_stun_attr_codec_addr_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int _status;
+ UCHAR pad;
+ UCHAR family;
+ UINT2 port;
+ UINT4 addr4;
+ struct in6_addr addr6;
+ nr_transport_addr *result = data;
+
+ if (nr_stun_decode(1, buf, buflen, &offset, &pad)
+ || nr_stun_decode(1, buf, buflen, &offset, &family))
+ ABORT(R_FAILED);
+
+ switch (family) {
+ case NR_STUN_IPV4_FAMILY:
+ if (attrlen != 8) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen);
+ ABORT(R_FAILED);
+ }
+
+ if (nr_stun_decode_htons(buf, buflen, &offset, &port)
+ || nr_stun_decode_htonl(buf, buflen, &offset, &addr4))
+ ABORT(R_FAILED);
+
+ if (nr_ip4_port_to_transport_addr(addr4, port, IPPROTO_UDP, result))
+ ABORT(R_FAILED);
+ break;
+
+ case NR_STUN_IPV6_FAMILY:
+ if (attrlen != 20) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen);
+ ABORT(R_FAILED);
+ }
+
+ if (nr_stun_decode_htons(buf, buflen, &offset, &port)
+ || nr_stun_decode(16, buf, buflen, &offset, addr6.s6_addr))
+ ABORT(R_FAILED);
+
+ if (nr_ip6_port_to_transport_addr(&addr6, port, IPPROTO_UDP, result))
+ ABORT(R_FAILED);
+ break;
+
+ default:
+ r_log(NR_LOG_STUN, LOG_WARNING, "Illegal address family: %d", family);
+ ABORT(R_FAILED);
+ break;
+ }
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_addr = {
+ "addr",
+ nr_stun_attr_codec_addr_print,
+ nr_stun_attr_codec_addr_encode,
+ nr_stun_attr_codec_addr_decode
+};
+
+static int
+nr_stun_attr_codec_data_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ nr_stun_attr_data *d = data;
+ r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)d->data, d->length);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_data_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ nr_stun_attr_data *d = data;
+ int start = offset;
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(d->length , buflen, buf, &offset)
+ || nr_stun_encode(d->data, d->length , buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_data_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int _status;
+ nr_stun_attr_data *result = data;
+
+ /* -1 because it is going to be null terminated just to be safe */
+ if (attrlen >= (sizeof(result->data) - 1)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Too much data: %d bytes", attrlen);
+ ABORT(R_FAILED);
+ }
+
+ if (nr_stun_decode(attrlen, buf, buflen, &offset, result->data))
+ ABORT(R_FAILED);
+
+ result->length = attrlen;
+ result->data[attrlen] = '\0'; /* just to be nice */
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_data = {
+ "data",
+ nr_stun_attr_codec_data_print,
+ nr_stun_attr_codec_data_encode,
+ nr_stun_attr_codec_data_decode
+};
+
+static int
+nr_stun_attr_codec_error_code_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ nr_stun_attr_error_code *error_code = data;
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %d %s",
+ msg, attr_info->name, error_code->number,
+ error_code->reason);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_error_code_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ nr_stun_attr_error_code *error_code = data;
+ int start = offset;
+ int length = strlen(error_code->reason);
+ UCHAR pad[2] = { 0 };
+ UCHAR class = error_code->number / 100;
+ UCHAR number = error_code->number % 100;
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(4 + length , buflen, buf, &offset)
+ || nr_stun_encode(pad, 2 , buflen, buf, &offset)
+ || nr_stun_encode(&class, 1 , buflen, buf, &offset)
+ || nr_stun_encode(&number, 1 , buflen, buf, &offset)
+ || nr_stun_encode((UCHAR*)error_code->reason, length, buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_error_code_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int _status;
+ nr_stun_attr_error_code *result = data;
+ UCHAR pad[2];
+ UCHAR class;
+ UCHAR number;
+ int size_reason;
+
+ if (nr_stun_decode(2, buf, buflen, &offset, pad)
+ || nr_stun_decode(1, buf, buflen, &offset, &class)
+ || nr_stun_decode(1, buf, buflen, &offset, &number))
+ ABORT(R_FAILED);
+
+ result->number = (class * 100) + number;
+
+ size_reason = attrlen - 4;
+
+ /* -1 because the string will be null terminated */
+ if (size_reason > (sizeof(result->reason) - 1)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Reason is too large, truncating");
+ /* don't fail, but instead truncate the reason */
+ size_reason = sizeof(result->reason) - 1;
+ }
+
+ if (nr_stun_decode(size_reason, buf, buflen, &offset, (UCHAR*)result->reason))
+ ABORT(R_FAILED);
+ result->reason[size_reason] = '\0';
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_error_code = {
+ "error_code",
+ nr_stun_attr_codec_error_code_print,
+ nr_stun_attr_codec_error_code_encode,
+ nr_stun_attr_codec_error_code_decode
+};
+
+static int
+nr_stun_attr_codec_fingerprint_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ nr_stun_attr_fingerprint *fingerprint = data;
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %08x", msg, attr_info->name, fingerprint->checksum);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_fingerprint_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ UINT4 checksum;
+ nr_stun_attr_fingerprint *fingerprint = data;
+ nr_stun_message_header *header = (nr_stun_message_header*)buf;
+
+ /* the length must include the FINGERPRINT attribute when computing
+ * the fingerprint */
+ header->length = ntohs(header->length);
+ header->length += 8; /* Fingerprint */
+ header->length = htons(header->length);
+
+ if (r_crc32((char*)buf, offset, &checksum)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint");
+ return R_FAILED;
+ }
+
+ fingerprint->checksum = checksum ^ 0x5354554e;
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum);
+
+ fingerprint->valid = 1;
+ return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen);
+}
+
+static int
+nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int r,_status;
+ nr_stun_attr_fingerprint *fingerprint = data;
+ nr_stun_message_header *header = (nr_stun_message_header*)buf;
+ int length;
+ UINT4 checksum;
+
+ if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum)))
+ ABORT(r);
+
+ offset -= 4; /* rewind to before the length and type fields */
+
+ /* the length must include the FINGERPRINT attribute when computing
+ * the fingerprint */
+ length = offset; /* right before FINGERPRINT */
+ length -= sizeof(*header); /* remove header length */
+ length += 8; /* add length of Fingerprint */
+ header->length = htons(length);
+
+ /* make sure FINGERPRINT is final attribute in message */
+ assert(length + sizeof(*header) == buflen);
+
+ if (r_crc32((char*)buf, offset, &checksum)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint");
+ ABORT(R_FAILED);
+ }
+
+ fingerprint->valid = (fingerprint->checksum == (checksum ^ 0x5354554e));
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", (checksum ^ 0x5354554e));
+ if (! fingerprint->valid)
+ r_log(NR_LOG_STUN, LOG_WARNING, "Invalid FINGERPRINT %08x", fingerprint->checksum);
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_fingerprint = {
+ "fingerprint",
+ nr_stun_attr_codec_fingerprint_print,
+ nr_stun_attr_codec_fingerprint_encode,
+ nr_stun_attr_codec_fingerprint_decode
+};
+
+static int
+nr_stun_attr_codec_flag_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: on", msg, attr_info->name);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_flag_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int start = offset;
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(0 , buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_flag_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ if (attrlen != 0) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Illegal flag length: %d", attrlen);
+ return R_FAILED;
+ }
+
+ return 0;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_flag = {
+ "flag",
+ nr_stun_attr_codec_flag_print,
+ nr_stun_attr_codec_flag_encode,
+ nr_stun_attr_codec_flag_decode
+};
+
+static int
+nr_stun_attr_codec_message_integrity_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ nr_stun_attr_message_integrity *integrity = data;
+ r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)integrity->hash, sizeof(integrity->hash));
+ return 0;
+}
+
+static int
+nr_stun_compute_message_integrity(UCHAR *buf, int offset, UCHAR *password, int passwordlen, UCHAR *computedHMAC)
+{
+ int r,_status;
+ UINT2 hold;
+ UINT2 length;
+ nr_stun_message_header *header;
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Computing MESSAGE-INTEGRITY");
+
+ header = (nr_stun_message_header*)buf;
+ hold = header->length;
+
+ /* adjust the length of the message */
+ length = offset;
+ length -= sizeof(*header);
+ length += 24; /* for MESSAGE-INTEGRITY attribute */
+ header->length = htons(length);
+
+ if ((r=nr_crypto_hmac_sha1((UCHAR*)password, passwordlen,
+ buf, offset, computedHMAC)))
+ ABORT(r);
+
+ r_dump(NR_LOG_STUN, LOG_DEBUG, "Computed MESSAGE-INTEGRITY ", (char*)computedHMAC, 20);
+
+ _status=0;
+ abort:
+ header->length = hold;
+ return _status;
+}
+
+static int
+nr_stun_attr_codec_message_integrity_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int start = offset;
+ nr_stun_attr_message_integrity *integrity = data;
+
+ if (nr_stun_compute_message_integrity(buf, offset, integrity->password, integrity->passwordlen, integrity->hash))
+ return R_FAILED;
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(sizeof(integrity->hash) , buflen, buf, &offset)
+ || nr_stun_encode(integrity->hash, sizeof(integrity->hash) , buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_message_integrity_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int _status;
+ int start;
+ nr_stun_attr_message_integrity *result = data;
+ UCHAR computedHMAC[20];
+
+ result->valid = 0;
+
+ if (attrlen != 20) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "%s must be 20 bytes, not %d", attr_info->name, attrlen);
+ ABORT(R_FAILED);
+ }
+
+ start = offset - 4; /* rewind to before the length and type fields */
+ if (start < 0)
+ ABORT(R_INTERNAL);
+
+ if (nr_stun_decode(attrlen, buf, buflen, &offset, result->hash))
+ ABORT(R_FAILED);
+
+ if (result->unknown_user) {
+ result->valid = 0;
+ }
+ else {
+ if (nr_stun_compute_message_integrity(buf, start, result->password, result->passwordlen, computedHMAC))
+ ABORT(R_FAILED);
+
+ assert(sizeof(computedHMAC) == sizeof(result->hash));
+
+ result->valid = (memcmp(computedHMAC, result->hash, 20) == 0);
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_message_integrity = {
+ "message_integrity",
+ nr_stun_attr_codec_message_integrity_print,
+ nr_stun_attr_codec_message_integrity_encode,
+ nr_stun_attr_codec_message_integrity_decode
+};
+
+static int
+nr_stun_attr_codec_noop_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ return SKIP_ATTRIBUTE_DECODE;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_noop = {
+ "NOOP",
+ 0, /* ignore, never print these attributes */
+ 0, /* ignore, never encode these attributes */
+ nr_stun_attr_codec_noop_decode
+};
+
+static int
+nr_stun_attr_codec_quoted_string_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s",
+ msg, attr_info->name, (char*)data);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_quoted_string_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+//TODO: !nn! syntax check, conversion if not quoted already?
+//We'll just restrict this in the API -- EKR
+ return nr_stun_attr_codec_string.encode(attr_info, data, offset, buflen, buf, attrlen);
+}
+
+static int
+nr_stun_attr_codec_quoted_string_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+//TODO: !nn! I don't see any need to unquote this but we may
+//find one later -- EKR
+ return nr_stun_attr_codec_string.decode(attr_info, attrlen, buf, offset, buflen, data);
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_quoted_string = {
+ "quoted_string",
+ nr_stun_attr_codec_quoted_string_print,
+ nr_stun_attr_codec_quoted_string_encode,
+ nr_stun_attr_codec_quoted_string_decode
+};
+
+static int
+nr_stun_attr_codec_string_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s",
+ msg, attr_info->name, (char*)data);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_string_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int start = offset;
+ char *str = data;
+ int length = strlen(str);
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(length , buflen, buf, &offset)
+ || nr_stun_encode((UCHAR*)str, length , buflen, buf, &offset))
+ return R_FAILED;
+
+ *attrlen = offset - start;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_string_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int _status;
+ char *result = data;
+
+ /* actual enforcement of the specific string size happens elsewhere */
+ if (attrlen >= NR_STUN_MAX_STRING_SIZE) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "String is too large: %d bytes", attrlen);
+ ABORT(R_FAILED);
+ }
+
+ if (nr_stun_decode(attrlen, buf, buflen, &offset, (UCHAR*)result))
+ ABORT(R_FAILED);
+ result[attrlen] = '\0'; /* just to be nice */
+
+ if (strlen(result) != attrlen) {
+ /* stund 0.96 sends a final null in the Server attribute, so
+ * only error if the null appears anywhere else in a string */
+ if (strlen(result) != attrlen-1) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Error in string: %zd/%d", strlen(result), attrlen);
+ ABORT(R_FAILED);
+ }
+ }
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_string = {
+ "string",
+ nr_stun_attr_codec_string_print,
+ nr_stun_attr_codec_string_encode,
+ nr_stun_attr_codec_string_decode
+};
+
+static int
+nr_stun_attr_codec_unknown_attributes_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ nr_stun_attr_unknown_attributes *unknown_attributes = data;
+ char type[9];
+ char str[64 + (NR_STUN_MAX_UNKNOWN_ATTRIBUTES * sizeof(type))];
+ int i;
+
+ snprintf(str, sizeof(str), "%s %s:", msg, attr_info->name);
+ for (i = 0; i < unknown_attributes->num_attributes; ++i) {
+ snprintf(type, sizeof(type), "%s 0x%04x", ((i>0)?",":""), unknown_attributes->attribute[i]);
+ strlcat(str, type, sizeof(str));
+ }
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s", str);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_unknown_attributes_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ int _status;
+ int start = offset;
+ nr_stun_attr_unknown_attributes *unknown_attributes = data;
+ int length = (2 * unknown_attributes->num_attributes);
+ int i;
+
+ if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes);
+ ABORT(R_FAILED);
+ }
+
+ if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset)
+ || nr_stun_encode_htons(length , buflen, buf, &offset))
+ ABORT(R_FAILED);
+
+ for (i = 0; i < unknown_attributes->num_attributes; ++i) {
+ if (nr_stun_encode_htons(unknown_attributes->attribute[i], buflen, buf, &offset))
+ ABORT(R_FAILED);
+ }
+
+ *attrlen = offset - start;
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+static int
+nr_stun_attr_codec_unknown_attributes_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int _status;
+ nr_stun_attr_unknown_attributes *unknown_attributes = data;
+ int i;
+ UINT2 *a;
+
+ if ((attrlen % 4) != 0) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attribute is illegal size: %d", attrlen);
+ ABORT(R_REJECTED);
+ }
+
+ unknown_attributes->num_attributes = attrlen / 2;
+
+ if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes);
+ ABORT(R_REJECTED);
+ }
+
+ for (i = 0; i < unknown_attributes->num_attributes; ++i) {
+ a = &(unknown_attributes->attribute[i]);
+ if (nr_stun_decode_htons(buf, buflen, &offset, a))
+ return R_FAILED;
+ }
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes = {
+ "unknown_attributes",
+ nr_stun_attr_codec_unknown_attributes_print,
+ nr_stun_attr_codec_unknown_attributes_encode,
+ nr_stun_attr_codec_unknown_attributes_decode
+};
+
+static int
+nr_stun_attr_codec_xor_mapped_address_print(nr_stun_attr_info *attr_info, char *msg, void *data)
+{
+ nr_stun_attr_xor_mapped_address *xor_mapped_address = data;
+ r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s (unmasked) %s (masked)",
+ msg, attr_info->name,
+ xor_mapped_address->unmasked.as_string,
+ xor_mapped_address->masked.as_string);
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_xor_mapped_address_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen)
+{
+ nr_stun_attr_xor_mapped_address *xor_mapped_address = data;
+ nr_stun_message_header *header = (nr_stun_message_header*)buf;
+ UINT4 magic_cookie;
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string);
+
+ /* this needs to be the magic cookie in the header and not
+ * the MAGIC_COOKIE constant because if we're talking to
+ * older servers (that don't have a magic cookie) they use
+ * message ID for this */
+ magic_cookie = ntohl(header->magic_cookie);
+
+ nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->unmasked, &xor_mapped_address->masked);
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string);
+
+ if (nr_stun_attr_codec_addr.encode(attr_info, &xor_mapped_address->masked, offset, buflen, buf, attrlen))
+ return R_FAILED;
+
+ return 0;
+}
+
+static int
+nr_stun_attr_codec_xor_mapped_address_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+{
+ int r,_status;
+ nr_stun_attr_xor_mapped_address *xor_mapped_address = data;
+ nr_stun_message_header *header = (nr_stun_message_header*)buf;
+ UINT4 magic_cookie;
+
+ if ((r=nr_stun_attr_codec_addr.decode(attr_info, attrlen, buf, offset, buflen, &xor_mapped_address->masked)))
+ ABORT(r);
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string);
+
+ /* this needs to be the magic cookie in the header and not
+ * the MAGIC_COOKIE constant because if we're talking to
+ * older servers (that don't have a magic cookie) they use
+ * message ID for this */
+ magic_cookie = ntohl(header->magic_cookie);
+
+ nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->masked, &xor_mapped_address->unmasked);
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string);
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address = {
+ "xor_mapped_address",
+ nr_stun_attr_codec_xor_mapped_address_print,
+ nr_stun_attr_codec_xor_mapped_address_encode,
+ nr_stun_attr_codec_xor_mapped_address_decode
+};
+
+nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address = {
+ "xor_mapped_address",
+ nr_stun_attr_codec_xor_mapped_address_print,
+ 0, /* never encode this type */
+ nr_stun_attr_codec_xor_mapped_address_decode
+};
+
+nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address = {
+ "xor_peer_address",
+ nr_stun_attr_codec_xor_mapped_address_print,
+ nr_stun_attr_codec_xor_mapped_address_encode,
+ nr_stun_attr_codec_xor_mapped_address_decode
+};
+
+#define NR_ADD_STUN_ATTRIBUTE(type, name, codec, illegal) \
+ { (type), (name), &(codec), illegal },
+
+#define NR_ADD_STUN_ATTRIBUTE_IGNORE(type, name) \
+ { (type), (name), &nr_stun_attr_codec_noop, 0 },
+
+
+static nr_stun_attr_info attrs[] = {
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ALTERNATE_SERVER, "ALTERNATE-SERVER", nr_stun_attr_codec_addr, 0)
+#ifdef USE_STUND_0_96
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_CHANGE_REQUEST, "CHANGE-REQUEST", nr_stun_attr_codec_UINT4, 0)
+#endif
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ERROR_CODE, "ERROR-CODE", nr_stun_attr_codec_error_code, nr_stun_attr_error_code_illegal)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_FINGERPRINT, "FINGERPRINT", nr_stun_attr_codec_fingerprint, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MAPPED_ADDRESS, "MAPPED-ADDRESS", nr_stun_attr_codec_addr, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MESSAGE_INTEGRITY, "MESSAGE-INTEGRITY", nr_stun_attr_codec_message_integrity, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_NONCE, "NONCE", nr_stun_attr_codec_quoted_string, nr_stun_attr_nonce_illegal)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REALM, "REALM", nr_stun_attr_codec_quoted_string, nr_stun_attr_realm_illegal)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_SERVER, "SERVER", nr_stun_attr_codec_string, nr_stun_attr_server_illegal)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, "UNKNOWN-ATTRIBUTES", nr_stun_attr_codec_unknown_attributes, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USERNAME, "USERNAME", nr_stun_attr_codec_string, nr_stun_attr_username_illegal)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_MAPPED_ADDRESS, "XOR-MAPPED-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0)
+
+#ifdef USE_ICE
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLED, "ICE-CONTROLLED", nr_stun_attr_codec_UINT8, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLING, "ICE-CONTROLLING", nr_stun_attr_codec_UINT8, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_PRIORITY, "PRIORITY", nr_stun_attr_codec_UINT4, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USE_CANDIDATE, "USE-CANDIDATE", nr_stun_attr_codec_flag, 0)
+#endif
+
+#ifdef USE_TURN
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_DATA, "DATA", nr_stun_attr_codec_data, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_LIFETIME, "LIFETIME", nr_stun_attr_codec_UINT4, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_RELAY_ADDRESS, "XOR-RELAY-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_PEER_ADDRESS, "XOR-PEER-ADDRESS", nr_stun_attr_codec_xor_peer_address, 0)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REQUESTED_TRANSPORT, "REQUESTED-TRANSPORT", nr_stun_attr_codec_UCHAR, 0)
+#endif /* USE_TURN */
+
+ /* for backwards compatibilty */
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS, "Old XOR-MAPPED-ADDRESS", nr_stun_attr_codec_old_xor_mapped_address, 0)
+#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
+ NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_RESPONSE_ADDRESS, "RESPONSE-ADDRESS")
+ NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_SOURCE_ADDRESS, "SOURCE-ADDRESS")
+ NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_CHANGED_ADDRESS, "CHANGED-ADDRESS")
+ NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_PASSWORD, "PASSWORD")
+#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
+};
+
+
+int
+nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info)
+{
+ int _status;
+ int i;
+
+ *info = 0;
+ for (i = 0; i < sizeof(attrs)/sizeof(*attrs); ++i) {
+ if (type == attrs[i].type) {
+ *info = &attrs[i];
+ break;
+ }
+ }
+
+ if (*info == 0)
+ ABORT(R_NOT_FOUND);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_stun_fix_attribute_ordering(nr_stun_message *msg)
+{
+ nr_stun_message_attribute *message_integrity;
+ nr_stun_message_attribute *fingerprint;
+
+ /* 2nd to the last */
+ if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &message_integrity)) {
+ TAILQ_REMOVE(&msg->attributes, message_integrity, entry);
+ TAILQ_INSERT_TAIL(&msg->attributes, message_integrity, entry);
+ }
+
+ /* last */
+ if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &fingerprint)) {
+ TAILQ_REMOVE(&msg->attributes, fingerprint, entry);
+ TAILQ_INSERT_TAIL(&msg->attributes, fingerprint, entry);
+ }
+
+ return 0;
+}
+
+#ifdef SANITY_CHECKS
+static void sanity_check_encoding_stuff(nr_stun_message *msg)
+{
+ nr_stun_message_attribute *attr = 0;
+ int padding_bytes;
+ int l;
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Starting to sanity check encoding");
+
+ l = 0;
+ TAILQ_FOREACH(attr, &msg->attributes, entry) {
+ padding_bytes = 0;
+ if ((attr->length % 4) != 0) {
+ padding_bytes = 4 - (attr->length % 4);
+ }
+ assert(attr->length == (attr->encoding_length - (4 + padding_bytes)));
+ assert(((void*)attr->encoding) == (msg->buffer + 20 + l));
+ l += attr->encoding_length;
+ assert((l % 4) == 0);
+ }
+ assert(l == msg->header.length);
+}
+#endif /* SANITY_CHECKS */
+
+
+int
+nr_stun_encode_message(nr_stun_message *msg)
+{
+ int r,_status;
+ int length_offset;
+ int length_offset_hold;
+ nr_stun_attr_info *attr_info;
+ nr_stun_message_attribute *attr;
+ int padding_bytes;
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Encoding STUN message");
+
+ nr_stun_fix_attribute_ordering(msg);
+
+ msg->name = nr_stun_msg_type(msg->header.type);
+ msg->length = 0;
+ msg->header.length = 0;
+
+ if ((r=nr_stun_encode_htons(msg->header.type, sizeof(msg->buffer), msg->buffer, &msg->length)))
+ ABORT(r);
+ if (msg->name)
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: %s", msg->name);
+ else
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: 0x%03x", msg->header.type);
+
+ /* grab the offset to be used later to re-write the header length field */
+ length_offset_hold = msg->length;
+
+ if ((r=nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &msg->length)))
+ ABORT(r);
+
+ if ((r=nr_stun_encode_htonl(msg->header.magic_cookie, sizeof(msg->buffer), msg->buffer, &msg->length)))
+ ABORT(r);
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Cookie: %08x", msg->header.magic_cookie);
+
+ if ((r=nr_stun_encode((UCHAR*)(&msg->header.id), sizeof(msg->header.id), sizeof(msg->buffer), msg->buffer, &msg->length)))
+ ABORT(r);
+ r_dump(NR_LOG_STUN, LOG_DEBUG, "Encoded ID", (void*)&msg->header.id, sizeof(msg->header.id));
+
+ TAILQ_FOREACH(attr, &msg->attributes, entry) {
+ if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Unrecognized attribute: 0x%04x", attr->type);
+ ABORT(R_INTERNAL);
+ }
+
+ attr->name = attr_info->name;
+ attr->type_name = attr_info->codec->name;
+ attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[msg->length];
+
+ if (attr_info->codec->encode != 0) {
+ if ((r=attr_info->codec->encode(attr_info, &attr->u, msg->length, sizeof(msg->buffer), msg->buffer, &attr->encoding_length))) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Unable to encode %s", attr_info->name);
+ ABORT(r);
+ }
+
+ msg->length += attr->encoding_length;
+ attr->length = attr->encoding_length - 4; /* -4 for type and length fields */
+
+ if (attr_info->illegal) {
+ if ((r=attr_info->illegal(attr_info, attr->length, &attr->u)))
+ ABORT(r);
+ }
+
+ attr_info->codec->print(attr_info, "Encoded", &attr->u);
+
+ if ((attr->length % 4) == 0) {
+ padding_bytes = 0;
+ }
+ else {
+ padding_bytes = 4 - (attr->length % 4);
+ nr_stun_encode((UCHAR*)"\0\0\0\0", padding_bytes, sizeof(msg->buffer), msg->buffer, &msg->length);
+ attr->encoding_length += padding_bytes;
+ }
+
+ msg->header.length += attr->encoding_length;
+ length_offset = length_offset_hold;
+ (void)nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &length_offset);
+ }
+ else {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing encode function for attribute: %s", attr_info->name);
+ }
+ }
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Length: %d", msg->header.length);
+
+ assert(msg->length < NR_STUN_MAX_MESSAGE_SIZE);
+
+#ifdef SANITY_CHECKS
+ sanity_check_encoding_stuff(msg);
+#endif /* SANITY_CHECKS */
+
+ _status=0;
+abort:
+ return _status;
+}
+
+int
+nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg)
+{
+ int r,_status;
+ int offset;
+ int size;
+ int padding_bytes;
+ nr_stun_message_attribute *attr;
+ nr_stun_attr_info *attr_info;
+ Data *password;
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Parsing STUN message of %d bytes", msg->length);
+
+ if (!TAILQ_EMPTY(&msg->attributes))
+ ABORT(R_BAD_ARGS);
+
+ if (sizeof(nr_stun_message_header) > msg->length) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Message too small");
+ ABORT(R_FAILED);
+ }
+
+ memcpy(&msg->header, msg->buffer, sizeof(msg->header));
+ msg->header.type = ntohs(msg->header.type);
+ msg->header.length = ntohs(msg->header.length);
+ msg->header.magic_cookie = ntohl(msg->header.magic_cookie);
+
+ msg->name = nr_stun_msg_type(msg->header.type);
+
+ if (msg->name)
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: %s", msg->name);
+ else
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: 0x%03x", msg->header.type);
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Length: %d", msg->header.length);
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Cookie: %08x", msg->header.magic_cookie);
+ r_dump(NR_LOG_STUN, LOG_DEBUG, "Parsed ID", (void*)&msg->header.id, sizeof(msg->header.id));
+
+ if (msg->header.length + sizeof(msg->header) != msg->length) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Inconsistent message header length: %d/%d",
+ msg->header.length, msg->length);
+ ABORT(R_FAILED);
+ }
+
+ size = msg->header.length;
+
+ if ((size % 4) != 0) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message size: %d", msg->header.length);
+ ABORT(R_FAILED);
+ }
+
+ offset = sizeof(msg->header);
+
+ while (size > 0) {
+ r_log(NR_LOG_STUN, LOG_DEBUG, "size = %d", size);
+
+ if (size < 4) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message length: %d", size);
+ ABORT(R_FAILED);
+ }
+
+ if ((r=nr_stun_message_attribute_create(msg, &attr)))
+ ABORT(R_NO_MEMORY);
+
+ attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[offset];
+ attr->type = ntohs(attr->encoding->type);
+ attr->length = ntohs(attr->encoding->length);
+ attr->encoding_length = attr->length + 4;
+
+ if ((attr->length % 4) != 0) {
+ padding_bytes = 4 - (attr->length % 4);
+ attr->encoding_length += padding_bytes;
+ }
+
+ if ((attr->encoding_length) > size) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attribute length larger than remaining message size: %d/%d", attr->encoding_length, size);
+ ABORT(R_FAILED);
+ }
+
+ if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) {
+ if (attr->type <= 0x7FFF)
+ ++msg->comprehension_required_unknown_attributes;
+ else
+ ++msg->comprehension_optional_unknown_attributes;
+ r_log(NR_LOG_STUN, LOG_INFO, "Unrecognized attribute: 0x%04x", attr->type);
+ }
+ else {
+ attr_info->name = attr_info->name;
+ attr->type_name = attr_info->codec->name;
+
+ if (attr->type == NR_STUN_ATTR_MESSAGE_INTEGRITY) {
+ if (get_password && get_password(arg, msg, &password) == 0) {
+ if (password->len > sizeof(attr->u.message_integrity.password)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Password too long: %d bytes", password->len);
+ ABORT(R_FAILED);
+ }
+
+ memcpy(attr->u.message_integrity.password, password->data, password->len);
+ attr->u.message_integrity.passwordlen = password->len;
+ }
+ else {
+ /* set to user "not found" */
+ attr->u.message_integrity.unknown_user = 1;
+ }
+ }
+ else if (attr->type == NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS) {
+ attr->type = NR_STUN_ATTR_XOR_MAPPED_ADDRESS;
+ r_log(NR_LOG_STUN, LOG_INFO, "Translating obsolete XOR-MAPPED-ADDRESS type");
+ }
+
+ if ((r=attr_info->codec->decode(attr_info, attr->length, msg->buffer, offset+4, msg->length, &attr->u))) {
+ if (r == SKIP_ATTRIBUTE_DECODE) {
+ r_log(NR_LOG_STUN, LOG_INFO, "Skipping %s", attr_info->name);
+ }
+ else {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Unable to parse %s", attr_info->name);
+ }
+
+ attr->invalid = 1;
+ }
+ else {
+ attr_info->codec->print(attr_info, "Parsed", &attr->u);
+
+#ifdef USE_STUN_PEDANTIC
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Before pedantic attr_info checks");
+ if (attr_info->illegal) {
+ if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Failed pedantic attr_info checks");
+ ABORT(r);
+ }
+ }
+ r_log(NR_LOG_STUN, LOG_DEBUG, "After pedantic attr_info checks");
+#endif /* USE_STUN_PEDANTIC */
+ }
+ }
+
+ offset += attr->encoding_length;
+ size -= attr->encoding_length;
+ }
+
+#ifdef SANITY_CHECKS
+ sanity_check_encoding_stuff(msg);
+#endif /* SANITY_CHECKS */
+
+ _status=0;
+ abort:
+ return _status;
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_codec.h b/media/mtransport/third_party/nICEr/src/stun/stun_codec.h
new file mode 100644
index 000000000..53e64334c
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_codec.h
@@ -0,0 +1,78 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _stun_codec_h
+#define _stun_codec_h
+
+#include "stun_msg.h"
+
+typedef struct nr_stun_attr_info_ nr_stun_attr_info;
+
+typedef struct nr_stun_attr_codec_ {
+ char *name;
+ int (*print)(nr_stun_attr_info *attr_info, char *msg, void *data);
+ int (*encode)(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen);
+ int (*decode)(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data);
+} nr_stun_attr_codec;
+
+struct nr_stun_attr_info_ {
+ UINT2 type;
+ char *name;
+ nr_stun_attr_codec *codec;
+ int (*illegal)(nr_stun_attr_info *attr_info, int attrlen, void *data);
+};
+
+extern nr_stun_attr_codec nr_stun_attr_codec_UINT4;
+extern nr_stun_attr_codec nr_stun_attr_codec_UINT8;
+extern nr_stun_attr_codec nr_stun_attr_codec_addr;
+extern nr_stun_attr_codec nr_stun_attr_codec_bytes;
+extern nr_stun_attr_codec nr_stun_attr_codec_data;
+extern nr_stun_attr_codec nr_stun_attr_codec_error_code;
+extern nr_stun_attr_codec nr_stun_attr_codec_fingerprint;
+extern nr_stun_attr_codec nr_stun_attr_codec_flag;
+extern nr_stun_attr_codec nr_stun_attr_codec_message_integrity;
+extern nr_stun_attr_codec nr_stun_attr_codec_noop;
+extern nr_stun_attr_codec nr_stun_attr_codec_quoted_string;
+extern nr_stun_attr_codec nr_stun_attr_codec_string;
+extern nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes;
+extern nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address;
+extern nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address;
+extern nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address;
+
+
+int nr_stun_encode_message(nr_stun_message *msg);
+int nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_hint.c b/media/mtransport/third_party/nICEr/src/stun/stun_hint.c
new file mode 100644
index 000000000..941b14d64
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_hint.c
@@ -0,0 +1,249 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_hint.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+
+#include <errno.h>
+#include <csi_platform.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <stdlib.h>
+#include <io.h>
+#include <time.h>
+#else /* UNIX */
+#include <string.h>
+#endif /* end UNIX */
+#include <assert.h>
+
+#include "stun.h"
+
+
+/* returns 0 if it's not a STUN message
+ * 1 if it's likely to be a STUN message
+ * 2 if it's super likely to be a STUN message
+ * 3 if it really is a STUN message */
+int
+nr_is_stun_message(UCHAR *buf, int len)
+{
+ const UINT4 cookie = htonl(NR_STUN_MAGIC_COOKIE);
+ const UINT4 cookie2 = htonl(NR_STUN_MAGIC_COOKIE2);
+#if 0
+ nr_stun_message msg;
+#endif
+ UINT2 type;
+ nr_stun_encoded_attribute* attr;
+ unsigned int attrLen;
+ int atrType;
+
+ if (sizeof(nr_stun_message_header) > len)
+ return 0;
+
+ if ((buf[0] & (0x80|0x40)) != 0)
+ return 0;
+
+ memcpy(&type, buf, 2);
+ type = ntohs(type);
+
+ switch (type) {
+ case NR_STUN_MSG_BINDING_REQUEST:
+ case NR_STUN_MSG_BINDING_INDICATION:
+ case NR_STUN_MSG_BINDING_RESPONSE:
+ case NR_STUN_MSG_BINDING_ERROR_RESPONSE:
+
+#ifdef USE_TURN
+ case NR_STUN_MSG_ALLOCATE_REQUEST:
+ case NR_STUN_MSG_ALLOCATE_RESPONSE:
+ case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE:
+ case NR_STUN_MSG_REFRESH_REQUEST:
+ case NR_STUN_MSG_REFRESH_RESPONSE:
+ case NR_STUN_MSG_REFRESH_ERROR_RESPONSE:
+ case NR_STUN_MSG_PERMISSION_REQUEST:
+ case NR_STUN_MSG_PERMISSION_RESPONSE:
+ case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE:
+ case NR_STUN_MSG_CHANNEL_BIND_REQUEST:
+ case NR_STUN_MSG_CHANNEL_BIND_RESPONSE:
+ case NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE:
+ case NR_STUN_MSG_SEND_INDICATION:
+ case NR_STUN_MSG_DATA_INDICATION:
+#ifdef NR_STUN_MSG_CONNECT_REQUEST
+ case NR_STUN_MSG_CONNECT_REQUEST:
+#endif
+#ifdef NR_STUN_MSG_CONNECT_RESPONSE
+ case NR_STUN_MSG_CONNECT_RESPONSE:
+#endif
+#ifdef NR_STUN_MSG_CONNECT_ERROR_RESPONSE
+ case NR_STUN_MSG_CONNECT_ERROR_RESPONSE:
+#endif
+#ifdef NR_STUN_MSG_CONNECT_STATUS_INDICATION
+ case NR_STUN_MSG_CONNECT_STATUS_INDICATION:
+#endif
+#endif /* USE_TURN */
+
+ /* ok so far, continue */
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ if (!memcmp(&cookie2, &buf[4], sizeof(UINT4))) {
+ /* return here because if it's an old-style message then there will
+ * not be a fingerprint in the message */
+ return 1;
+ }
+
+ if (memcmp(&cookie, &buf[4], sizeof(UINT4)))
+ return 0;
+
+ /* the magic cookie was right, so it's pretty darn likely that what we've
+ * got here is a STUN message */
+
+ attr = (nr_stun_encoded_attribute*)(buf + (len - 8));
+ attrLen = ntohs(attr->length);
+ atrType = ntohs(attr->type);
+
+ if (atrType != NR_STUN_ATTR_FINGERPRINT || attrLen != 4)
+ return 1;
+
+ /* the fingerprint is in the right place and looks sane, so we can be quite
+ * sure we've got a STUN message */
+
+#if 0
+/* nevermind this check ... there's a reasonable chance that a NAT has modified
+ * the message (and thus the fingerprint check will fail), but it's still an
+ * otherwise-perfectly-good STUN message, so skip the check since we're going
+ * to return "true" whether the check succeeds or fails */
+
+ if (nr_stun_parse_attr_UINT4(buf + (len - 4), attrLen, &msg.fingerprint))
+ return 2;
+
+
+ if (nr_stun_compute_fingerprint(buf, len - 8, &computedFingerprint))
+ return 2;
+
+ if (msg.fingerprint.number != computedFingerprint)
+ return 2;
+
+ /* and the fingerprint is good, so it's gotta be a STUN message */
+#endif
+
+ return 3;
+}
+
+int
+nr_is_stun_request_message(UCHAR *buf, int len)
+{
+ UINT2 type;
+
+ if (sizeof(nr_stun_message_header) > len)
+ return 0;
+
+ if (!nr_is_stun_message(buf, len))
+ return 0;
+
+ memcpy(&type, buf, 2);
+ type = ntohs(type);
+
+ return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_REQUEST;
+}
+
+int
+nr_is_stun_indication_message(UCHAR *buf, int len)
+{
+ UINT2 type;
+
+ if (sizeof(nr_stun_message_header) > len)
+ return 0;
+
+ if (!nr_is_stun_message(buf, len))
+ return 0;
+
+ memcpy(&type, buf, 2);
+ type = ntohs(type);
+
+ return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_INDICATION;
+}
+
+int
+nr_is_stun_response_message(UCHAR *buf, int len)
+{
+ UINT2 type;
+
+ if (sizeof(nr_stun_message_header) > len)
+ return 0;
+
+ if (!nr_is_stun_message(buf, len))
+ return 0;
+
+ memcpy(&type, buf, 2);
+ type = ntohs(type);
+
+ return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_RESPONSE
+ || NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_ERROR_RESPONSE;
+}
+
+int
+nr_has_stun_cookie(UCHAR *buf, int len)
+{
+ static UINT4 cookie;
+
+ cookie = htonl(NR_STUN_MAGIC_COOKIE);
+
+ if (sizeof(nr_stun_message_header) > len)
+ return 0;
+
+ if (memcmp(&cookie, &buf[4], sizeof(UINT4)))
+ return 0;
+
+ return 1;
+}
+
+int
+nr_stun_message_length(UCHAR *buf, int buf_len, int *msg_len)
+{
+ nr_stun_message_header *hdr;
+
+ if (!nr_is_stun_message(buf, buf_len))
+ return(R_BAD_DATA);
+
+ hdr = (nr_stun_message_header *)buf;
+
+ *msg_len = ntohs(hdr->length);
+
+ return(0);
+}
+
+
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_hint.h b/media/mtransport/third_party/nICEr/src/stun/stun_hint.h
new file mode 100644
index 000000000..4b285d31d
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_hint.h
@@ -0,0 +1,44 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef _stun_hint_h
+#define _stun_hint_h
+
+int nr_is_stun_message(UCHAR *buf, int len);
+int nr_is_stun_request_message(UCHAR *buf, int len);
+int nr_is_stun_response_message(UCHAR *buf, int len);
+int nr_is_stun_indication_message(UCHAR *buf, int len);
+int nr_has_stun_cookie(UCHAR *buf, int len);
+int nr_stun_message_length(UCHAR *buf, int len, int *length);
+
+#endif
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_msg.c b/media/mtransport/third_party/nICEr/src/stun/stun_msg.c
new file mode 100644
index 000000000..c34bb46b0
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_msg.c
@@ -0,0 +1,360 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_msg.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <errno.h>
+#include <csi_platform.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <stdlib.h>
+#include <io.h>
+#include <time.h>
+#else /* UNIX */
+#include <string.h>
+#endif /* end UNIX */
+#include <assert.h>
+
+#include "stun.h"
+
+
+int
+nr_stun_message_create(nr_stun_message **msg)
+{
+ int _status;
+ nr_stun_message *m = 0;
+
+ m = RCALLOC(sizeof(*m));
+ if (!m)
+ ABORT(R_NO_MEMORY);
+
+ TAILQ_INIT(&m->attributes);
+
+ *msg = m;
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, int length)
+{
+ int r,_status;
+ nr_stun_message *m = 0;
+
+ if (length > sizeof(m->buffer)) {
+ ABORT(R_BAD_DATA);
+ }
+
+ if ((r=nr_stun_message_create(&m)))
+ ABORT(r);
+
+ memcpy(m->buffer, buffer, length);
+ m->length = length;
+
+ *msg = m;
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_stun_message_destroy(nr_stun_message **msg)
+{
+ int _status;
+ nr_stun_message_attribute_head *attrs;
+ nr_stun_message_attribute *attr;
+
+ if (msg && *msg) {
+ attrs = &(*msg)->attributes;
+ while (!TAILQ_EMPTY(attrs)) {
+ attr = TAILQ_FIRST(attrs);
+ nr_stun_message_attribute_destroy(*msg, &attr);
+ }
+
+ RFREE(*msg);
+
+ *msg = 0;
+ }
+
+ _status=0;
+/* abort: */
+ return(_status);
+}
+
+int
+nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr)
+{
+ int _status;
+ nr_stun_message_attribute *a = 0;
+
+ a = RCALLOC(sizeof(*a));
+ if (!a)
+ ABORT(R_NO_MEMORY);
+
+ TAILQ_INSERT_TAIL(&msg->attributes, a, entry);
+
+ *attr = a;
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr)
+{
+ int _status;
+ nr_stun_message_attribute *a = 0;
+
+ if (attr && *attr) {
+ a = *attr;
+ TAILQ_REMOVE(&msg->attributes, a, entry);
+
+ RFREE(a);
+
+ *attr = 0;
+ }
+
+ _status=0;
+/* abort: */
+ return(_status);
+}
+
+int
+nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute)
+{
+ nr_stun_message_attribute *attr = 0;
+
+ if (attribute)
+ *attribute = 0;
+
+ TAILQ_FOREACH(attr, &msg->attributes, entry) {
+ if (attr->type == type)
+ break;
+ }
+
+ if (!attr || attr->invalid)
+ return 0; /* does not have */
+
+ if (attribute)
+ *attribute = attr;
+
+ return 1; /* has */
+}
+
+#define NR_STUN_MESSAGE_ADD_ATTRIBUTE(__type, __code) \
+ { \
+ int r,_status; \
+ nr_stun_message_attribute *attr = 0; \
+ if ((r=nr_stun_message_attribute_create(msg, &attr))) \
+ ABORT(r); \
+ attr->type = (__type); \
+ { __code } \
+ _status=0; \
+ abort: \
+ if (_status){ \
+ nr_stun_message_attribute_destroy(msg, &attr); \
+ } \
+ return(_status); \
+ }
+
+
+int
+nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_ALTERNATE_SERVER,
+ {
+ if ((r=nr_transport_addr_copy(&attr->u.alternate_server, alternate_server)))
+ ABORT(r);
+ }
+)
+
+int
+nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_ERROR_CODE,
+ {
+ attr->u.error_code.number = number;
+ strlcpy(attr->u.error_code.reason, reason, sizeof(attr->u.error_code.reason));
+ }
+)
+
+int
+nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_FINGERPRINT,
+ {}
+)
+
+int
+nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_MESSAGE_INTEGRITY,
+ {
+ if (sizeof(attr->u.message_integrity.password) < password->len)
+ ABORT(R_BAD_DATA);
+
+ memcpy(attr->u.message_integrity.password, password->data, password->len);
+ attr->u.message_integrity.passwordlen = password->len;
+ }
+)
+
+int
+nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_NONCE,
+ { strlcpy(attr->u.nonce, nonce, sizeof(attr->u.nonce)); }
+)
+
+int
+nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_REALM,
+ { strlcpy(attr->u.realm, realm, sizeof(attr->u.realm)); }
+)
+
+int
+nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_SERVER,
+ { strlcpy(attr->u.server_name, server_name, sizeof(attr->u.server_name)); }
+)
+
+int
+nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_UNKNOWN_ATTRIBUTES,
+ { memcpy(&attr->u.unknown_attributes, unknown_attributes, sizeof(attr->u.unknown_attributes)); }
+)
+
+int
+nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_USERNAME,
+ { strlcpy(attr->u.username, username, sizeof(attr->u.username)); }
+)
+
+int
+nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR protocol)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_REQUESTED_TRANSPORT,
+ { attr->u.requested_transport = protocol; }
+)
+
+int
+nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_XOR_MAPPED_ADDRESS,
+ {
+ if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, mapped_address)))
+ ABORT(r);
+ }
+)
+
+int
+nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_XOR_PEER_ADDRESS,
+ {
+ if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, peer_address)))
+ ABORT(r);
+ }
+)
+
+#ifdef USE_ICE
+int
+nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_ICE_CONTROLLED,
+ { attr->u.ice_controlled = ice_controlled; }
+)
+
+int
+nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_ICE_CONTROLLING,
+ { attr->u.ice_controlling = ice_controlling; }
+)
+
+int
+nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_PRIORITY,
+ { attr->u.priority = priority; }
+)
+
+int
+nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_USE_CANDIDATE,
+ {}
+)
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+int
+nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length)
+
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_DATA,
+ {
+ if (length > NR_STUN_MAX_MESSAGE_SIZE)
+ ABORT(R_BAD_ARGS);
+
+ memcpy(attr->u.data.data, data, length);
+ attr->u.data.length=length;
+ }
+)
+
+int
+nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_LIFETIME,
+ { attr->u.lifetime_secs = lifetime_secs; }
+)
+
+#endif /* USE_TURN */
+
+#ifdef USE_STUND_0_96
+int
+nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request)
+NR_STUN_MESSAGE_ADD_ATTRIBUTE(
+ NR_STUN_ATTR_OLD_CHANGE_REQUEST,
+ { attr->u.change_request = change_request; }
+)
+#endif /* USE_STUND_0_96 */
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_msg.h b/media/mtransport/third_party/nICEr/src/stun/stun_msg.h
new file mode 100644
index 000000000..927ce2e3c
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_msg.h
@@ -0,0 +1,206 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _stun_msg_h
+#define _stun_msg_h
+
+#include "csi_platform.h"
+#include "nr_api.h"
+#include "transport_addr.h"
+
+#define NR_STUN_MAX_USERNAME_BYTES 513
+#define NR_STUN_MAX_ERROR_CODE_REASON_BYTES 763
+#define NR_STUN_MAX_ERROR_CODE_REASON_CHARS 128
+#define NR_STUN_MAX_REALM_BYTES 763
+#define NR_STUN_MAX_REALM_CHARS 128
+#define NR_STUN_MAX_NONCE_BYTES 763
+#define NR_STUN_MAX_NONCE_CHARS 128
+#define NR_STUN_MAX_SERVER_BYTES 763
+#define NR_STUN_MAX_SERVER_CHARS 128
+#define NR_STUN_MAX_STRING_SIZE 763 /* any possible string */
+#define NR_STUN_MAX_UNKNOWN_ATTRIBUTES 16
+#define NR_STUN_MAX_MESSAGE_SIZE 2048
+
+#define NR_STUN_MAGIC_COOKIE 0x2112A442
+#define NR_STUN_MAGIC_COOKIE2 0xc5cb4e1d /* used recognize old stun messages */
+
+typedef struct { UCHAR octet[12]; } UINT12;
+
+typedef struct nr_stun_attr_error_code_ {
+ UINT2 number;
+ char reason[NR_STUN_MAX_ERROR_CODE_REASON_BYTES+1]; /* +1 for \0 */
+} nr_stun_attr_error_code;
+
+typedef struct nr_stun_attr_fingerprint_ {
+ UINT4 checksum;
+ int valid;
+} nr_stun_attr_fingerprint;
+
+typedef struct nr_stun_attr_message_integrity_ {
+ UCHAR hash[20];
+ int unknown_user;
+ UCHAR password[1024];
+ int passwordlen;
+ int valid;
+} nr_stun_attr_message_integrity;
+
+typedef struct nr_stun_attr_unknown_attributes_ {
+ UINT2 attribute[NR_STUN_MAX_UNKNOWN_ATTRIBUTES];
+ int num_attributes;
+} nr_stun_attr_unknown_attributes;
+
+typedef struct nr_stun_attr_xor_mapped_address_ {
+ nr_transport_addr masked;
+ nr_transport_addr unmasked;
+} nr_stun_attr_xor_mapped_address;
+
+typedef struct nr_stun_attr_data_ {
+ UCHAR data[NR_STUN_MAX_MESSAGE_SIZE];
+ int length;
+} nr_stun_attr_data;
+
+
+typedef struct nr_stun_encoded_attribute_ {
+ UINT2 type;
+ UINT2 length;
+ UCHAR value[NR_STUN_MAX_MESSAGE_SIZE];
+} nr_stun_encoded_attribute;
+
+typedef struct nr_stun_message_attribute_ {
+ UINT2 type;
+ UINT2 length;
+ union {
+ nr_transport_addr address;
+ nr_transport_addr alternate_server;
+ nr_stun_attr_error_code error_code;
+ nr_stun_attr_fingerprint fingerprint;
+ nr_transport_addr mapped_address;
+ nr_stun_attr_message_integrity message_integrity;
+ char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */
+ char realm[NR_STUN_MAX_REALM_BYTES+1]; /* +1 for \0 */
+ nr_stun_attr_xor_mapped_address relay_address;
+ char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */
+ nr_stun_attr_unknown_attributes unknown_attributes;
+ char username[NR_STUN_MAX_USERNAME_BYTES+1]; /* +1 for \0 */
+ nr_stun_attr_xor_mapped_address xor_mapped_address;
+
+#ifdef USE_ICE
+ UINT4 priority;
+ UINT8 ice_controlled;
+ UINT8 ice_controlling;
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+ UINT4 lifetime_secs;
+ nr_transport_addr remote_address;
+ UCHAR requested_transport;
+ nr_stun_attr_data data;
+#endif /* USE_TURN */
+
+#ifdef USE_STUND_0_96
+ UINT4 change_request;
+#endif /* USE_STUND_0_96 */
+
+ /* make sure there's enough room here to place any possible
+ * attribute */
+ UCHAR largest_possible_attribute[NR_STUN_MAX_MESSAGE_SIZE];
+ } u;
+ nr_stun_encoded_attribute *encoding;
+ int encoding_length;
+ char *name;
+ char *type_name;
+ int invalid;
+ TAILQ_ENTRY(nr_stun_message_attribute_) entry;
+} nr_stun_message_attribute;
+
+typedef TAILQ_HEAD(nr_stun_message_attribute_head_,nr_stun_message_attribute_) nr_stun_message_attribute_head;
+
+typedef struct nr_stun_message_header_ {
+ UINT2 type;
+ UINT2 length;
+ UINT4 magic_cookie;
+ UINT12 id;
+} nr_stun_message_header;
+
+typedef struct nr_stun_message_ {
+ char *name;
+ UCHAR buffer[NR_STUN_MAX_MESSAGE_SIZE];
+ int length;
+ nr_stun_message_header header;
+ int comprehension_required_unknown_attributes;
+ int comprehension_optional_unknown_attributes;
+ nr_stun_message_attribute_head attributes;
+} nr_stun_message;
+
+int nr_stun_message_create(nr_stun_message **msg);
+int nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, int length);
+int nr_stun_message_destroy(nr_stun_message **msg);
+
+int nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr);
+int nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr);
+
+int nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute);
+
+int nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server);
+int nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason);
+int nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg);
+int nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password);
+int nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce);
+int nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm);
+int nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name);
+int nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes);
+int nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username);
+int nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address);
+
+#ifdef USE_ICE
+int nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled);
+int nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling);
+int nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority);
+int nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg);
+#endif /* USE_ICE */
+
+#ifdef USE_TURN
+int nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length);
+int nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs);
+int nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR transport);
+int
+nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address);
+#endif /* USE_TURN */
+
+#ifdef USE_STUND_0_96
+int nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request);
+#endif /* USE_STUND_0_96 */
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_proc.c b/media/mtransport/third_party/nICEr/src/stun/stun_proc.c
new file mode 100644
index 000000000..336100264
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_proc.c
@@ -0,0 +1,568 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_proc.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <errno.h>
+#include <csi_platform.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <stdlib.h>
+#include <io.h>
+#include <time.h>
+#else /* UNIX */
+#include <string.h>
+#endif /* end UNIX */
+#include <assert.h>
+
+#include "stun.h"
+#include "stun_reg.h"
+#include "registry.h"
+
+static int
+nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res);
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */
+int
+nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg)
+{
+ int _status;
+ nr_stun_message_attribute *attr;
+
+#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
+ /* if this message was generated by an RFC 3489 impementation,
+ * the call to nr_is_stun_message will fail, so skip that
+ * check and puke elsewhere if the message can't be decoded */
+ if (msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE
+ || msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE2) {
+#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
+ if (!nr_is_stun_message(msg->buffer, msg->length)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Not a STUN message");
+ ABORT(R_REJECTED);
+ }
+#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
+ }
+#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
+
+ if (req == 0) {
+ if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_REQUEST) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message type: %03x", msg->header.type);
+ ABORT(R_REJECTED);
+ }
+ }
+ else {
+ if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_RESPONSE
+ && NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_ERROR_RESPONSE) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message class: %03x", msg->header.type);
+ ABORT(R_REJECTED);
+ }
+
+ if (NR_STUN_GET_TYPE_METHOD(req->header.type) != NR_STUN_GET_TYPE_METHOD(msg->header.type)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"Inconsistent message method: %03x expected %03x", msg->header.type, req->header.type);
+ ABORT(R_REJECTED);
+ }
+
+ if (nr_stun_different_transaction(msg->buffer, msg->length, req)) {
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized STUN transaction");
+ ABORT(R_REJECTED);
+ }
+ }
+
+ switch (msg->header.magic_cookie) {
+ case NR_STUN_MAGIC_COOKIE:
+ /* basically draft-ietf-behave-rfc3489bis-10.txt S 6 rules */
+
+ if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &attr)
+ && !attr->u.fingerprint.valid) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Invalid fingerprint");
+ ABORT(R_REJECTED);
+ }
+
+ break;
+
+#ifdef USE_STUND_0_96
+ case NR_STUN_MAGIC_COOKIE2:
+ /* nothing to check in this case */
+ break;
+#endif /* USE_STUND_0_96 */
+
+ default:
+#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
+ /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
+#else
+#ifdef NDEBUG
+ /* in deployment builds we should always see a recognized magic cookie */
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing Magic Cookie");
+ ABORT(R_REJECTED);
+#else
+ /* ignore this condition because sometimes we like to pretend we're
+ * a server talking to old clients and their messages don't contain
+ * a magic cookie at all but rather the magic cookie field is part
+ * of their ID and therefore random */
+#endif /* NDEBUG */
+#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */
+ break;
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1 */
+int
+nr_stun_process_request(nr_stun_message *req, nr_stun_message *res)
+{
+ int _status;
+#ifdef USE_STUN_PEDANTIC
+ int r;
+ nr_stun_attr_unknown_attributes unknown_attributes = { { 0 } };
+ nr_stun_message_attribute *attr;
+
+ if (req->comprehension_required_unknown_attributes > 0) {
+ nr_stun_form_error_response(req, res, 420, "Unknown Attributes");
+
+ TAILQ_FOREACH(attr, &req->attributes, entry) {
+ if (attr->name == 0) {
+ /* unrecognized attribute */
+
+ /* should never happen, but truncate if it ever were to occur */
+ if (unknown_attributes.num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES)
+ break;
+
+ unknown_attributes.attribute[unknown_attributes.num_attributes++] = attr->type;
+ }
+ }
+
+ assert(req->comprehension_required_unknown_attributes + req->comprehension_optional_unknown_attributes == unknown_attributes.num_attributes);
+
+ if ((r=nr_stun_message_add_unknown_attributes_attribute(res, &unknown_attributes)))
+ ABORT(R_ALREADY);
+
+ ABORT(R_ALREADY);
+ }
+#endif /* USE_STUN_PEDANTIC */
+
+ _status=0;
+#ifdef USE_STUN_PEDANTIC
+ abort:
+#endif /* USE_STUN_PEDANTIC */
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.2 */
+int
+nr_stun_process_indication(nr_stun_message *ind)
+{
+ int _status;
+#ifdef USE_STUN_PEDANTIC
+
+ if (ind->comprehension_required_unknown_attributes > 0)
+ ABORT(R_REJECTED);
+#endif /* USE_STUN_PEDANTIC */
+
+ _status=0;
+#ifdef USE_STUN_PEDANTIC
+ abort:
+#endif /* USE_STUN_PEDANTIC */
+ return _status;
+}
+
+/* RFC5389 S 7.3.3, except that we *also* allow a MAPPED_ADDRESS
+ to compensate for a bug in Google's STUN server where it
+ always returns MAPPED_ADDRESS.
+
+ Mozilla bug: 888274.
+ */
+int
+nr_stun_process_success_response(nr_stun_message *res)
+{
+ int _status;
+
+#ifdef USE_STUN_PEDANTIC
+ if (res->comprehension_required_unknown_attributes > 0)
+ ABORT(R_REJECTED);
+#endif /* USE_STUN_PEDANTIC */
+
+ if (NR_STUN_GET_TYPE_METHOD(res->header.type) == NR_METHOD_BINDING) {
+ if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0) &&
+ ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing XOR-MAPPED-ADDRESS and MAPPED_ADDRESS");
+ ABORT(R_REJECTED);
+ }
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.4 */
+int
+nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code)
+{
+ int _status;
+ nr_stun_message_attribute *attr;
+
+ if (res->comprehension_required_unknown_attributes > 0) {
+ ABORT(R_REJECTED);
+ }
+
+ if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, &attr)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing ERROR-CODE");
+ ABORT(R_REJECTED);
+ }
+
+ *error_code = attr->u.error_code.number;
+
+ switch (attr->u.error_code.number / 100) {
+ case 3:
+ /* If the error code is 300 through 399, the client SHOULD consider
+ * the transaction as failed unless the ALTERNATE-SERVER extension is
+ * being used. See Section 11. */
+
+ if (attr->u.error_code.number == 300) {
+ if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ALTERNATE_SERVER, 0)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing ALTERNATE-SERVER");
+ ABORT(R_REJECTED);
+ }
+
+ /* draft-ietf-behave-rfc3489bis-10.txt S 11 */
+ if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY");
+ ABORT(R_REJECTED);
+ }
+
+ ABORT(R_RETRY);
+ }
+
+ ABORT(R_REJECTED);
+ break;
+
+ case 4:
+ /* If the error code is 400 through 499, the client declares the
+ * transaction failed; in the case of 420 (Unknown Attribute), the
+ * response should contain a UNKNOWN-ATTRIBUTES attribute that gives
+ * additional information. */
+ if (attr->u.error_code.number == 420)
+ ABORT(R_REJECTED);
+
+ /* it may be possible to restart given the info that was received in
+ * this response, so retry */
+ ABORT(R_RETRY);
+ break;
+
+ case 5:
+ /* If the error code is 500 through 599, the client MAY resend the
+ * request; clients that do so MUST limit the number of times they do
+ * this. */
+ /* let the retransmit mechanism handle resending the request */
+ break;
+
+ default:
+ ABORT(R_REJECTED);
+ break;
+ }
+
+ /* the spec says: "The client then does any processing specified by the authentication
+ * mechanism (see Section 10). This may result in a new transaction
+ * attempt." -- but this is handled already elsewhere, so needn't be repeated
+ * in this function */
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.2 */
+int
+nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg,
+ nr_stun_message *res)
+{
+ int _status;
+ nr_stun_message_attribute *attr;
+
+ switch (msg->header.magic_cookie) {
+ default:
+ /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
+ /* drop thru */
+ case NR_STUN_MAGIC_COOKIE:
+ if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
+ nr_stun_form_error_response(msg, res, 400, "Missing MESSAGE-INTEGRITY");
+ ABORT(R_ALREADY);
+ }
+
+ if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, 0)) {
+ nr_stun_form_error_response(msg, res, 400, "Missing USERNAME");
+ ABORT(R_ALREADY);
+ }
+
+ if (attr->u.message_integrity.unknown_user) {
+ nr_stun_form_error_response(msg, res, 401, "Unrecognized USERNAME");
+ ABORT(R_ALREADY);
+ }
+
+ if (!attr->u.message_integrity.valid) {
+ nr_stun_form_error_response(msg, res, 401, "Bad MESSAGE-INTEGRITY");
+ ABORT(R_ALREADY);
+ }
+
+ break;
+
+#ifdef USE_STUND_0_96
+ case NR_STUN_MAGIC_COOKIE2:
+ /* nothing to check in this case */
+ break;
+#endif /* USE_STUND_0_96 */
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.3 */
+int
+nr_stun_receive_response_short_term_auth(nr_stun_message *res)
+{
+ int _status;
+ nr_stun_message_attribute *attr;
+
+ switch (res->header.magic_cookie) {
+ default:
+ /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
+ /* drop thru */
+ case NR_STUN_MAGIC_COOKIE:
+ if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY");
+ ABORT(R_REJECTED);
+ }
+
+ if (!attr->u.message_integrity.valid) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY");
+ ABORT(R_REJECTED);
+ }
+
+ break;
+
+#ifdef USE_STUND_0_96
+ case NR_STUN_MAGIC_COOKIE2:
+ /* nothing to check in this case */
+ break;
+#endif /* USE_STUND_0_96 */
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+static int
+nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res)
+{
+ int r,_status;
+ char *realm = 0;
+ char *nonce;
+ UINT2 size;
+
+ if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_add_realm_attribute(res, realm)))
+ ABORT(r);
+
+ if (clnt) {
+ if (strlen(clnt->nonce) < 1)
+ new_nonce = 1;
+
+ if (new_nonce) {
+ if (NR_reg_get_uint2(NR_STUN_REG_PREF_SERVER_NONCE_SIZE, &size))
+ size = 48;
+
+ if (size > (sizeof(clnt->nonce) - 1))
+ size = sizeof(clnt->nonce) - 1;
+
+ nr_random_alphanum(clnt->nonce, size);
+ clnt->nonce[size] = '\0';
+ }
+
+ nonce = clnt->nonce;
+ }
+ else {
+ /* user is not known, so use a bogus nonce since there's no way to
+ * store a good nonce with the client-specific data -- this nonce
+ * will be recognized as stale if the client attempts another
+ * request */
+ nonce = "STALE";
+ }
+
+ if ((r=nr_stun_message_add_nonce_attribute(res, nonce)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+#ifdef USE_TURN
+assert(_status == 0); /* TODO: !nn! cleanup after I reimplmement TURN */
+#endif
+ RFREE(realm);
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.1 - 10.2.2 */
+int
+nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res)
+{
+ int r,_status;
+ nr_stun_message_attribute *mi;
+ nr_stun_message_attribute *n;
+ nr_stun_server_client *clnt = 0;
+
+ switch (req->header.magic_cookie) {
+ default:
+ /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
+ /* drop thru */
+ case NR_STUN_MAGIC_COOKIE:
+ if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)) {
+ nr_stun_form_error_response(req, res, 400, "Missing USERNAME");
+ nr_stun_add_realm_and_nonce(0, 0, res);
+ ABORT(R_ALREADY);
+ }
+
+ if ((r=nr_stun_get_message_client(ctx, req, &clnt))) {
+ nr_stun_form_error_response(req, res, 401, "Unrecognized USERNAME");
+ nr_stun_add_realm_and_nonce(0, 0, res);
+ ABORT(R_ALREADY);
+ }
+
+ if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, &mi)) {
+ nr_stun_form_error_response(req, res, 401, "Missing MESSAGE-INTEGRITY");
+ nr_stun_add_realm_and_nonce(0, clnt, res);
+ ABORT(R_ALREADY);
+ }
+
+ assert(!mi->u.message_integrity.unknown_user);
+
+ if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_REALM, 0)) {
+ nr_stun_form_error_response(req, res, 400, "Missing REALM");
+ ABORT(R_ALREADY);
+ }
+
+ if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_NONCE, &n)) {
+ nr_stun_form_error_response(req, res, 400, "Missing NONCE");
+ ABORT(R_ALREADY);
+ }
+
+ assert(sizeof(clnt->nonce) == sizeof(n->u.nonce));
+ if (strncmp(clnt->nonce, n->u.nonce, sizeof(n->u.nonce))) {
+ nr_stun_form_error_response(req, res, 438, "Stale NONCE");
+ nr_stun_add_realm_and_nonce(1, clnt, res);
+ ABORT(R_ALREADY);
+ }
+
+ if (!mi->u.message_integrity.valid) {
+ nr_stun_form_error_response(req, res, 401, "Bad MESSAGE-INTEGRITY");
+ nr_stun_add_realm_and_nonce(0, clnt, res);
+ ABORT(R_ALREADY);
+ }
+
+ break;
+
+#ifdef USE_STUND_0_96
+ case NR_STUN_MAGIC_COOKIE2:
+ /* nothing to do in this case */
+ break;
+#endif /* USE_STUND_0_96 */
+ }
+
+ _status=0;
+ abort:
+
+ return _status;
+}
+
+/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.3 */
+int
+nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx)
+{
+ int _status;
+ nr_stun_message_attribute *attr;
+
+ switch (res->header.magic_cookie) {
+ default:
+ /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */
+ /* drop thru */
+ case NR_STUN_MAGIC_COOKIE:
+ if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_REALM, &attr)) {
+ RFREE(ctx->realm);
+ ctx->realm = r_strdup(attr->u.realm);
+ if (!ctx->realm)
+ ABORT(R_NO_MEMORY);
+ }
+ else {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing REALM");
+ ABORT(R_REJECTED);
+ }
+
+ if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_NONCE, &attr)) {
+ RFREE(ctx->nonce);
+ ctx->nonce = r_strdup(attr->u.nonce);
+ if (!ctx->nonce)
+ ABORT(R_NO_MEMORY);
+ }
+ else {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Missing NONCE");
+ ABORT(R_REJECTED);
+ }
+
+ if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) {
+ if (!attr->u.message_integrity.valid) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY");
+ ABORT(R_REJECTED);
+ }
+ }
+
+ break;
+
+#ifdef USE_STUND_0_96
+ case NR_STUN_MAGIC_COOKIE2:
+ /* nothing to check in this case */
+ break;
+#endif /* USE_STUND_0_96 */
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_proc.h b/media/mtransport/third_party/nICEr/src/stun/stun_proc.h
new file mode 100644
index 000000000..597567077
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_proc.h
@@ -0,0 +1,53 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _stun_proc_h
+#define _stun_proc_h
+
+#include "stun.h"
+
+int nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg);
+int nr_stun_process_request(nr_stun_message *req, nr_stun_message *res);
+int nr_stun_process_indication(nr_stun_message *ind);
+int nr_stun_process_success_response(nr_stun_message *res);
+int nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code);
+
+int nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg, nr_stun_message *res);
+int nr_stun_receive_response_short_term_auth(nr_stun_message *res);
+
+int nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res);
+int nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_reg.h b/media/mtransport/third_party/nICEr/src/stun/stun_reg.h
new file mode 100644
index 000000000..2167fcd91
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_reg.h
@@ -0,0 +1,58 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _stun_reg_h
+#define _stun_reg_h
+#ifdef __cplusplus
+using namespace std;
+extern "C" {
+#endif /* __cplusplus */
+
+#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT "stun.client.retransmission_timeout"
+#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF "stun.client.retransmission_backoff_factor"
+#define NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS "stun.client.maximum_transmits"
+#define NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF "stun.client.final_retransmit_backoff"
+
+#define NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS "stun.allow_loopback"
+#define NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS "stun.allow_link_local"
+#define NR_STUN_REG_PREF_ADDRESS_PRFX "stun.address"
+#define NR_STUN_REG_PREF_SERVER_NAME "stun.server.name"
+#define NR_STUN_REG_PREF_SERVER_NONCE_SIZE "stun.server.nonce_size"
+#define NR_STUN_REG_PREF_SERVER_REALM "stun.server.realm"
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c
new file mode 100644
index 000000000..3bfa1674b
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c
@@ -0,0 +1,472 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_server_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <string.h>
+#include <assert.h>
+
+#include "nr_api.h"
+#include "stun.h"
+
+static int nr_stun_server_destroy_client(nr_stun_server_client *clnt);
+static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt);
+static int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res);
+
+
+int nr_stun_server_ctx_create(char *label, nr_socket *sock, nr_stun_server_ctx **ctxp)
+ {
+ int r,_status;
+ nr_stun_server_ctx *ctx=0;
+
+ if ((r=nr_stun_startup()))
+ ABORT(r);
+
+ if(!(ctx=RCALLOC(sizeof(nr_stun_server_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ if(!(ctx->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+ ctx->sock=sock;
+ nr_socket_getaddr(sock,&ctx->my_addr);
+
+ STAILQ_INIT(&ctx->clients);
+
+ *ctxp=ctx;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp)
+ {
+ nr_stun_server_ctx *ctx;
+ nr_stun_server_client *clnt1,*clnt2;
+
+ if(!ctxp || !*ctxp)
+ return(0);
+
+ ctx=*ctxp;
+
+ STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) {
+ nr_stun_server_destroy_client(clnt1);
+ }
+
+ nr_stun_server_destroy_client(ctx->default_client);
+
+ RFREE(ctx->label);
+ RFREE(ctx);
+
+ return(0);
+ }
+
+static int nr_stun_server_client_create(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg, nr_stun_server_client **clntp)
+ {
+ nr_stun_server_client *clnt=0;
+ int r,_status;
+
+ if(!(clnt=RCALLOC(sizeof(nr_stun_server_client))))
+ ABORT(R_NO_MEMORY);
+
+ if(!(clnt->label=r_strdup(client_label)))
+ ABORT(R_NO_MEMORY);
+
+ if(!(clnt->username=r_strdup(user)))
+ ABORT(R_NO_MEMORY);
+
+ if(r=r_data_copy(&clnt->password,pass))
+ ABORT(r);
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user);
+ clnt->stun_server_cb=cb;
+ clnt->cb_arg=cb_arg;
+
+ *clntp = clnt;
+ _status=0;
+ abort:
+ if(_status){
+ nr_stun_server_destroy_client(clnt);
+ }
+ return(_status);
+ }
+
+int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg)
+ {
+ int r,_status;
+ nr_stun_server_client *clnt;
+
+ if (r=nr_stun_server_client_create(ctx, client_label, user, pass, cb, cb_arg, &clnt))
+ ABORT(r);
+
+ STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg)
+ {
+ int r,_status;
+ nr_stun_server_client *clnt;
+
+ assert(!ctx->default_client);
+ if (ctx->default_client)
+ ABORT(R_INTERNAL);
+
+ if (r=nr_stun_server_client_create(ctx, "default_client", ufrag, pass, cb, cb_arg, &clnt))
+ ABORT(r);
+
+ ctx->default_client = clnt;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg)
+ {
+ nr_stun_server_client *clnt1,*clnt2;
+ int found = 0;
+
+ STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) {
+ if(clnt1->cb_arg == cb_arg) {
+ STAILQ_REMOVE(&ctx->clients, clnt1, nr_stun_server_client_, entry);
+ nr_stun_server_destroy_client(clnt1);
+ found++;
+ }
+ }
+
+ if (!found)
+ ERETURN(R_NOT_FOUND);
+
+ return 0;
+ }
+
+static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password)
+ {
+ int _status;
+ nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg;
+ nr_stun_server_client *clnt = 0;
+ nr_stun_message_attribute *username_attribute;
+
+ if ((nr_stun_get_message_client(ctx, msg, &clnt))) {
+ if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label);
+ ABORT(R_NOT_FOUND);
+ }
+
+ /* Although this is an exceptional condition, we'll already have seen a
+ * NOTICE-level log message about the unknown user, so additional log
+ * messages at any level higher than DEBUG are unnecessary. */
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username);
+ ABORT(R_NOT_FOUND);
+ }
+
+ *password = &clnt->password;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res)
+ {
+ int r,_status;
+
+ if (nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)
+ || !(auth_rule & NR_STUN_AUTH_RULE_OPTIONAL)) {
+ /* favor long term credentials over short term, if both are supported */
+
+ if (auth_rule & NR_STUN_AUTH_RULE_LONG_TERM) {
+ if ((r=nr_stun_receive_request_long_term_auth(req, ctx, res)))
+ ABORT(r);
+ }
+ else if (auth_rule & NR_STUN_AUTH_RULE_SHORT_TERM) {
+ if ((r=nr_stun_receive_request_or_indication_short_term_auth(req, res)))
+ ABORT(r);
+ }
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule)
+ {
+ int r,_status;
+ char string[256];
+ nr_stun_message *req = 0;
+ nr_stun_message *res = 0;
+ nr_stun_server_client *clnt = 0;
+ nr_stun_server_request info;
+ int error;
+ int dont_free = 0;
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
+
+ snprintf(string, sizeof(string)-1, "STUN-SERVER(%s): Received ", ctx->label);
+ r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len);
+
+ memset(&info,0,sizeof(info));
+
+ if ((r=nr_stun_message_create2(&req, (UCHAR*)msg, len)))
+ ABORT(r);
+
+ if ((r=nr_stun_message_create(&res)))
+ ABORT(r);
+
+ if ((r=nr_stun_decode_message(req, nr_stun_server_get_password, ctx))) {
+ /* RFC5389 S 7.3 says "If any errors are detected, the message is
+ * silently discarded." */
+#ifndef USE_STUN_PEDANTIC
+ /* ... but that seems like a bad idea, at least return a 400 so
+ * that the server isn't a black hole to the client */
+ nr_stun_form_error_response(req, res, 400, "Bad Request - Failed to decode request");
+ ABORT(R_ALREADY);
+#endif /* USE_STUN_PEDANTIC */
+ ABORT(R_REJECTED);
+ }
+
+ if ((r=nr_stun_receive_message(0, req))) {
+ /* RFC5389 S 7.3 says "If any errors are detected, the message is
+ * silently discarded." */
+#ifndef USE_STUN_PEDANTIC
+ /* ... but that seems like a bad idea, at least return a 400 so
+ * that the server isn't a black hole to the client */
+ nr_stun_form_error_response(req, res, 400, "Bad Request - Section 7.3 check failed");
+ ABORT(R_ALREADY);
+#endif /* USE_STUN_PEDANTIC */
+ ABORT(R_REJECTED);
+ }
+
+ if (NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_REQUEST
+ && NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_INDICATION) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Illegal message type: %04x",ctx->label,req->header.type);
+ /* RFC5389 S 7.3 says "If any errors are detected, the message is
+ * silently discarded." */
+#ifndef USE_STUN_PEDANTIC
+ /* ... but that seems like a bad idea, at least return a 400 so
+ * that the server isn't a black hole to the client */
+ nr_stun_form_error_response(req, res, 400, "Bad Request - Unsupported message type");
+ ABORT(R_ALREADY);
+#endif /* USE_STUN_PEDANTIC */
+ ABORT(R_REJECTED);
+ }
+
+ /* "The STUN agent then does any checks that are required by a
+ * authentication mechanism that the usage has specified" */
+ if ((r=nr_stun_server_process_request_auth_checks(ctx, req, auth_rule, res)))
+ ABORT(r);
+
+ if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) {
+ if ((r=nr_stun_process_indication(req)))
+ ABORT(r);
+ }
+ else {
+ if ((r=nr_stun_process_request(req, res)))
+ ABORT(r);
+ }
+
+ assert(res->header.type == 0);
+
+ clnt = 0;
+ if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_REQUEST) {
+ if ((nr_stun_get_message_client(ctx, req, &clnt))) {
+ if ((r=nr_stun_form_success_response(req, peer_addr, 0, res)))
+ ABORT(r);
+ }
+ else {
+ if ((r=nr_stun_form_success_response(req, peer_addr, &clnt->password, res)))
+ ABORT(r);
+ }
+ }
+
+ if(clnt && clnt->stun_server_cb){
+ r_log(NR_LOG_STUN,LOG_DEBUG,"Entering STUN server callback");
+
+ /* Set up the info */
+ if(r=nr_transport_addr_copy(&info.src_addr,peer_addr))
+ ABORT(r);
+
+ info.request = req;
+ info.response = res;
+
+ error = 0;
+ dont_free = 0;
+ if (clnt->stun_server_cb(clnt->cb_arg,ctx,sock,&info,&dont_free,&error)) {
+ if (error == 0)
+ error = 500;
+
+ nr_stun_form_error_response(req, res, error, "ICE Failure");
+ ABORT(R_ALREADY);
+ }
+ }
+
+ _status=0;
+ abort:
+ if (!res)
+ goto skip_response;
+
+ if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION)
+ goto skip_response;
+
+ /* Now respond */
+
+ if (_status != 0 && ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0))
+ nr_stun_form_error_response(req, res, 500, "Failed to specify error");
+
+ if ((r=nr_stun_server_send_response(ctx, sock, peer_addr, res, clnt))) {
+ r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Failed sending response (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
+ _status = R_FAILED;
+ }
+
+#if 0
+ /* EKR: suppressed these checks because if you have an error when
+ you are sending an error, things go wonky */
+#ifdef SANITY_CHECKS
+ if (_status == R_ALREADY) {
+ assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_ERROR_RESPONSE);
+ assert(nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0));
+ }
+ else {
+ assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_RESPONSE);
+ assert(!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0));
+ }
+#endif /* SANITY_CHECKS */
+#endif
+
+ if (0) {
+ skip_response:
+ _status = 0;
+ }
+
+ if (!dont_free) {
+ nr_stun_message_destroy(&res);
+ nr_stun_message_destroy(&req);
+ }
+
+ return(_status);
+ }
+
+static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt)
+ {
+ int r,_status;
+ Data *hmacPassword;
+ char string[256];
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(label=%s): Sending(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string);
+
+ if (clnt) {
+ hmacPassword = &clnt->password;
+ }
+ else {
+ hmacPassword = 0;
+ }
+
+ if ((r=nr_stun_encode_message(res))) {
+ /* should never happen */
+ r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Unable to encode message", ctx->label);
+ }
+ else {
+ snprintf(string, sizeof(string)-1, "STUN(%s): Sending to %s ", ctx->label, peer_addr->as_string);
+ r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)res->buffer, res->length);
+
+ if(r=nr_socket_sendto(sock?sock:ctx->sock,res->buffer,res->length,0,peer_addr))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_stun_server_destroy_client(nr_stun_server_client *clnt)
+ {
+ if (!clnt)
+ return 0;
+
+ RFREE(clnt->label);
+ RFREE(clnt->username);
+ r_data_zfree(&clnt->password);
+
+ RFREE(clnt);
+ return(0);
+ }
+
+int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **out)
+ {
+ int _status;
+ nr_stun_message_attribute *attr;
+ nr_stun_server_client *clnt=0;
+
+ if (! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, &attr)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label);
+ ABORT(R_NOT_FOUND);
+ }
+
+ STAILQ_FOREACH(clnt, &ctx->clients, entry) {
+ if (!strncmp(clnt->username, attr->u.username,
+ sizeof(attr->u.username)))
+ break;
+ }
+
+ if (!clnt && ctx->default_client) {
+ /* If we can't find a specific client see if this matches the default,
+ which means that the username starts with our ufrag.
+ */
+ char *colon = strchr(attr->u.username, ':');
+ if (colon && !strncmp(ctx->default_client->username,
+ attr->u.username,
+ colon - attr->u.username)) {
+ clnt = ctx->default_client;
+ r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Falling back to default client, username=: %s",ctx->label,attr->u.username);
+ }
+ }
+
+ if (!clnt) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Request from unknown user: %s",ctx->label,attr->u.username);
+ ABORT(R_NOT_FOUND);
+ }
+
+ *out = clnt;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.h b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.h
new file mode 100644
index 000000000..343e67606
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.h
@@ -0,0 +1,82 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _stun_server_ctx_h
+#define _stun_server_ctx_h
+
+typedef struct nr_stun_server_ctx_ nr_stun_server_ctx;
+typedef struct nr_stun_server_client_ nr_stun_server_client;
+
+#include "stun_util.h"
+
+
+typedef struct nr_stun_server_request_{
+ nr_transport_addr src_addr;
+ nr_stun_message *request;
+ nr_stun_message *response;
+} nr_stun_server_request;
+
+typedef int (*nr_stun_server_cb)(void *cb_arg, struct nr_stun_server_ctx_ *ctx,nr_socket *sock,nr_stun_server_request *req, int *dont_free, int *error);
+
+struct nr_stun_server_client_ {
+ char *label;
+ char *username;
+ Data password;
+ nr_stun_server_cb stun_server_cb;
+ void *cb_arg;
+ char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */
+ STAILQ_ENTRY(nr_stun_server_client_) entry;
+};
+
+typedef STAILQ_HEAD(nr_stun_server_client_head_, nr_stun_server_client_) nr_stun_server_client_head;
+
+struct nr_stun_server_ctx_ {
+ char *label;
+ nr_socket *sock;
+ nr_transport_addr my_addr;
+ nr_stun_server_client_head clients;
+ nr_stun_server_client *default_client;
+};
+
+
+int nr_stun_server_ctx_create(char *label, nr_socket *sock, nr_stun_server_ctx **ctxp);
+int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp);
+int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg);
+int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg);
+int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg);
+int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule);
+int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **clnt);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_util.c b/media/mtransport/third_party/nICEr/src/stun/stun_util.c
new file mode 100644
index 000000000..e347e542f
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_util.c
@@ -0,0 +1,322 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+static char *RCSSTRING __UNUSED__="$Id: stun_util.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#include <errno.h>
+#include <csi_platform.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <stdlib.h>
+#include <io.h>
+#include <time.h>
+#else /* UNIX */
+#include <string.h>
+#endif /* end UNIX */
+#include <assert.h>
+
+#include "stun.h"
+#include "stun_reg.h"
+#include "registry.h"
+#include "addrs.h"
+#include "transport_addr_reg.h"
+#include "nr_crypto.h"
+#include "hex.h"
+
+
+int NR_LOG_STUN = 0;
+
+int
+nr_stun_startup(void)
+{
+ int r,_status;
+
+ if ((r=r_log_register("stun", &NR_LOG_STUN)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+int
+nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to)
+{
+ int _status;
+
+ switch (from->ip_version) {
+ case NR_IPV4:
+ nr_ip4_port_to_transport_addr(
+ (ntohl(from->u.addr4.sin_addr.s_addr) ^ magicCookie),
+ (ntohs(from->u.addr4.sin_port) ^ (magicCookie>>16)),
+ from->protocol, to);
+ break;
+ case NR_IPV6:
+ {
+ union {
+ unsigned char addr[16];
+ UINT4 addr32[4];
+ } maskedAddr;
+
+ maskedAddr.addr32[0] = htonl(magicCookie); /* Passed in host byte order */
+ memcpy(&maskedAddr.addr32[1], transactionId.octet, sizeof(transactionId));
+
+ /* We now have the mask in network byte order */
+ /* Xor the address in network byte order */
+ for (int i = 0; i < sizeof(maskedAddr); ++i) {
+ maskedAddr.addr[i] ^= from->u.addr6.sin6_addr.s6_addr[i];
+ }
+
+ nr_ip6_port_to_transport_addr(
+ (struct in6_addr*)&maskedAddr,
+ (ntohs(from->u.addr6.sin6_port) ^ (magicCookie>>16)),
+ from->protocol, to);
+ }
+ break;
+ default:
+ assert(0);
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+int
+nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count)
+{
+ int r,_status;
+ //NR_registry *children = 0;
+
+ *count = 0;
+
+ if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count)))
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+
+ if (*count == 0) {
+ char allow_loopback;
+ char allow_link_local;
+
+ if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback))) {
+ if (r == R_NOT_FOUND)
+ allow_loopback = 0;
+ else
+ ABORT(r);
+ }
+
+ if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, &allow_link_local))) {
+ if (r == R_NOT_FOUND)
+ allow_link_local = 0;
+ else
+ ABORT(r);
+ }
+
+ if ((r=nr_stun_get_addrs(addrs, maxaddrs, !allow_loopback, !allow_link_local, count)))
+ ABORT(r);
+
+ goto done;
+ }
+
+ if (*count >= maxaddrs) {
+ r_log(NR_LOG_STUN, LOG_INFO, "Address list truncated from %d to %d", *count, maxaddrs);
+ *count = maxaddrs;
+ }
+
+#if 0
+ if (*count > 0) {
+ /* TODO(ekr@rtfm.com): Commented out 2012-07-26.
+
+ This code is currently not used in Firefox and needs to be
+ ported to 64-bit */
+ children = RCALLOC((*count + 10) * sizeof(*children));
+ if (!children)
+ ABORT(R_NO_MEMORY);
+
+ assert(sizeof(size_t) == sizeof(*count));
+
+ if ((r=NR_reg_get_children(NR_STUN_REG_PREF_ADDRESS_PRFX, children, (size_t)(*count + 10), (size_t*)count)))
+ ABORT(r);
+
+ for (i = 0; i < *count; ++i) {
+ if ((r=nr_reg_get_transport_addr(children[i], 0, &addrs[i].addr)))
+ ABORT(r);
+ }
+ }
+#endif
+
+ done:
+
+ _status=0;
+ abort:
+ //RFREE(children);
+ return _status;
+}
+
+int
+nr_stun_different_transaction(UCHAR *msg, int len, nr_stun_message *req)
+{
+ int _status;
+ nr_stun_message_header header;
+ char reqid[44];
+ char msgid[44];
+ int len2;
+
+ if (sizeof(header) > len)
+ ABORT(R_FAILED);
+
+ assert(sizeof(header.id) == sizeof(UINT12));
+
+ memcpy(&header, msg, sizeof(header));
+
+ if (memcmp(&req->header.id, &header.id, sizeof(header.id))) {
+ nr_nbin2hex((UCHAR*)&req->header.id, sizeof(req->header.id), reqid, sizeof(reqid), &len2);
+ nr_nbin2hex((UCHAR*)&header.id, sizeof(header.id), msgid, sizeof(msgid), &len2);
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Mismatched message IDs %s/%s", reqid, msgid);
+ ABORT(R_NOT_FOUND);
+ }
+
+ _status=0;
+ abort:
+ return _status;
+}
+
+char*
+nr_stun_msg_type(int type)
+{
+ char *ret = 0;
+
+ switch (type) {
+ case NR_STUN_MSG_BINDING_REQUEST:
+ ret = "BINDING-REQUEST";
+ break;
+ case NR_STUN_MSG_BINDING_INDICATION:
+ ret = "BINDING-INDICATION";
+ break;
+ case NR_STUN_MSG_BINDING_RESPONSE:
+ ret = "BINDING-RESPONSE";
+ break;
+ case NR_STUN_MSG_BINDING_ERROR_RESPONSE:
+ ret = "BINDING-ERROR-RESPONSE";
+ break;
+
+#ifdef USE_TURN
+ case NR_STUN_MSG_ALLOCATE_REQUEST:
+ ret = "ALLOCATE-REQUEST";
+ break;
+ case NR_STUN_MSG_ALLOCATE_RESPONSE:
+ ret = "ALLOCATE-RESPONSE";
+ break;
+ case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE:
+ ret = "ALLOCATE-ERROR-RESPONSE";
+ break;
+ case NR_STUN_MSG_REFRESH_REQUEST:
+ ret = "REFRESH-REQUEST";
+ break;
+ case NR_STUN_MSG_REFRESH_RESPONSE:
+ ret = "REFRESH-RESPONSE";
+ break;
+ case NR_STUN_MSG_REFRESH_ERROR_RESPONSE:
+ ret = "REFRESH-ERROR-RESPONSE";
+ break;
+ case NR_STUN_MSG_SEND_INDICATION:
+ ret = "SEND-INDICATION";
+ break;
+ case NR_STUN_MSG_DATA_INDICATION:
+ ret = "DATA-INDICATION";
+ break;
+ case NR_STUN_MSG_PERMISSION_REQUEST:
+ ret = "PERMISSION-REQUEST";
+ break;
+ case NR_STUN_MSG_PERMISSION_RESPONSE:
+ ret = "PERMISSION-RESPONSE";
+ break;
+ case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE:
+ ret = "PERMISSION-ERROR-RESPONSE";
+ break;
+#endif /* USE_TURN */
+
+ default:
+ /* ret remains 0 */
+ break;
+ }
+
+ return ret;
+}
+
+int
+nr_random_alphanum(char *alphanum, int size)
+{
+ static char alphanums[256] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };
+ int i;
+
+ nr_crypto_random_bytes((UCHAR*)alphanum, size);
+
+ /* now convert from binary to alphanumeric */
+ for (i = 0; i < size; ++i)
+ alphanum[i] = alphanums[(UCHAR)alphanum[i]];
+
+ return 0;
+}
diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_util.h b/media/mtransport/third_party/nICEr/src/stun/stun_util.h
new file mode 100644
index 000000000..c57e84d12
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_util.h
@@ -0,0 +1,56 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _stun_util_h
+#define _stun_util_h
+
+#include "stun.h"
+#include "local_addr.h"
+
+extern int NR_LOG_STUN;
+
+int nr_stun_startup(void);
+
+int nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to);
+
+int nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count);
+
+int nr_stun_different_transaction(UCHAR *msg, int len, nr_stun_message *req);
+
+char* nr_stun_msg_type(int type);
+
+int nr_random_alphanum(char *alphanum, int size);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c
new file mode 100644
index 000000000..c873f6faf
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c
@@ -0,0 +1,1056 @@
+/*
+ Copyright (c) 2007, Adobe Systems, Incorporated
+ Copyright (c) 2013, Mozilla
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: turn_client_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $";
+
+#ifdef USE_TURN
+
+#include <assert.h>
+#include <string.h>
+
+#include "nr_api.h"
+#include "r_time.h"
+#include "async_timer.h"
+#include "nr_socket_buffered_stun.h"
+#include "stun.h"
+#include "turn_client_ctx.h"
+
+int NR_LOG_TURN = 0;
+
+#define TURN_MAX_PENDING_BYTES 32000
+
+#define TURN_RTO 100 /* Hardcoded RTO estimate */
+#define TURN_LIFETIME_REQUEST_SECONDS 3600 /* One hour */
+#define TURN_USECS_PER_S 1000000
+#define TURN_REFRESH_SLACK_SECONDS 10 /* How long before expiry to refresh
+ allocations/permissions. The RFC 5766
+ Section 7 recommendation if 60 seconds,
+ but this is silly since the transaction
+ times out after about 5. */
+#define TURN_PERMISSION_LIFETIME_SECONDS 300 /* 5 minutes. From RFC 5766 2.3 */
+
+// Set to enable a temporary fix that will run the TURN reservation keep-alive
+// logic when data is received via a TURN relayed path: a DATA_INDICATION packet is received.
+// TODO(pkerr@mozilla.com) This should be replace/removed when bug 935806 is implemented.
+#define REFRESH_RESERVATION_ON_RECV 1
+
+static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int type,
+ NR_async_cb success_cb,
+ NR_async_cb failure_cb,
+ nr_turn_stun_ctx **ctxp);
+static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp);
+static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg);
+static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx,
+ char *realm, char *nonce);
+static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg);
+static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx,
+ nr_turn_stun_ctx **sctx);
+static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *ctx,
+ nr_turn_stun_ctx *sctx,
+ UINT4 lifetime);
+static int nr_turn_permission_create(nr_turn_client_ctx *ctx,
+ nr_transport_addr *addr,
+ nr_turn_permission **permp);
+static int nr_turn_permission_find(nr_turn_client_ctx *ctx,
+ nr_transport_addr *addr,
+ nr_turn_permission **permp);
+static int nr_turn_permission_destroy(nr_turn_permission **permp);
+static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg);
+static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *cb);
+static int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx,
+ nr_stun_message *req,
+ int flags);
+
+
+/* nr_turn_stun_ctx functions */
+static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int mode,
+ NR_async_cb success_cb,
+ NR_async_cb error_cb,
+ nr_turn_stun_ctx **ctxp)
+{
+ nr_turn_stun_ctx *sctx = 0;
+ int r,_status;
+ char label[256];
+
+ if (!(sctx=RCALLOC(sizeof(nr_turn_stun_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ /* TODO(ekr@rtfm.com): label by phase */
+ snprintf(label, sizeof(label), "%s:%s", tctx->label, ":TURN");
+
+ if ((r=nr_stun_client_ctx_create(label, tctx->sock, &tctx->turn_server_addr,
+ TURN_RTO, &sctx->stun))) {
+ ABORT(r);
+ }
+
+ /* Set the STUN auth parameters, but don't set authentication on.
+ For that we need the nonce, set in nr_turn_stun_set_auth_params.
+ */
+ sctx->stun->auth_params.username=tctx->username;
+ INIT_DATA(sctx->stun->auth_params.password,
+ tctx->password->data, tctx->password->len);
+
+ sctx->tctx=tctx;
+ sctx->success_cb=success_cb;
+ sctx->error_cb=error_cb;
+ sctx->mode=mode;
+ sctx->last_error_code=0;
+
+ /* Add ourselves to the tctx's list */
+ STAILQ_INSERT_TAIL(&tctx->stun_ctxs, sctx, entry);
+ *ctxp=sctx;
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_turn_stun_ctx_destroy(&sctx);
+ }
+ return(_status);
+}
+
+/* Note: this function does not pull us off the tctx's list. */
+static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp)
+{
+ nr_turn_stun_ctx *ctx;
+
+ if (!ctxp || !*ctxp)
+ return 0;
+
+ ctx = *ctxp;
+ *ctxp = 0;
+
+ nr_stun_client_ctx_destroy(&ctx->stun);
+ RFREE(ctx->realm);
+ RFREE(ctx->nonce);
+
+ RFREE(ctx);
+
+ return 0;
+}
+
+static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx,
+ char *realm, char *nonce)
+{
+ int _status;
+
+ RFREE(ctx->realm);
+ RFREE(ctx->nonce);
+
+ assert(realm);
+ if (!realm)
+ ABORT(R_BAD_ARGS);
+ ctx->realm=r_strdup(realm);
+ if (!ctx->realm)
+ ABORT(R_NO_MEMORY);
+
+ assert(nonce);
+ if (!nonce)
+ ABORT(R_BAD_ARGS);
+ ctx->nonce=r_strdup(nonce);
+ if (!ctx->nonce)
+ ABORT(R_NO_MEMORY);
+
+ RFREE(ctx->stun->realm);
+ ctx->stun->realm = r_strdup(ctx->realm);
+ if (!ctx->stun->realm)
+ ABORT(R_NO_MEMORY);
+
+ ctx->stun->auth_params.realm = ctx->realm;
+ ctx->stun->auth_params.nonce = ctx->nonce;
+ ctx->stun->auth_params.authenticate = 1; /* May already be 1 */
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static int nr_turn_stun_ctx_start(nr_turn_stun_ctx *ctx)
+{
+ int r, _status;
+ nr_turn_client_ctx *tctx = ctx->tctx;
+
+ if ((r=nr_stun_client_reset(ctx->stun))) {
+ r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't reset STUN",
+ tctx->label);
+ ABORT(r);
+ }
+
+ if ((r=nr_stun_client_start(ctx->stun, ctx->mode, nr_turn_stun_ctx_cb, ctx))) {
+ r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN",
+ tctx->label);
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ return _status;
+}
+
+static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg)
+{
+ int r, _status;
+ nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
+
+ ctx->last_error_code = ctx->stun->error_code;
+
+ switch (ctx->stun->state) {
+ case NR_STUN_CLIENT_STATE_DONE:
+ /* Save the realm and nonce */
+ if (ctx->stun->realm && (!ctx->tctx->realm || strcmp(ctx->stun->realm,
+ ctx->tctx->realm))) {
+ RFREE(ctx->tctx->realm);
+ ctx->tctx->realm = r_strdup(ctx->stun->realm);
+ if (!ctx->tctx->realm)
+ ABORT(R_NO_MEMORY);
+ }
+ if (ctx->stun->nonce && (!ctx->tctx->nonce || strcmp(ctx->stun->nonce,
+ ctx->tctx->nonce))) {
+ RFREE(ctx->tctx->nonce);
+ ctx->tctx->nonce = r_strdup(ctx->stun->nonce);
+ if (!ctx->tctx->nonce)
+ ABORT(R_NO_MEMORY);
+ }
+
+ ctx->retry_ct=0;
+ ctx->success_cb(0, 0, ctx);
+ break;
+
+ case NR_STUN_CLIENT_STATE_FAILED:
+ /* Special case: if this is an authentication error,
+ we retry once. This allows the 401/438 nonce retry
+ paradigm. After that, we fail */
+ /* TODO(ekr@rtfm.com): 401 needs a #define */
+ /* TODO(ekr@rtfm.com): Add alternate-server (Mozilla bug 857688) */
+ if (ctx->stun->error_code == 401 || ctx->stun->error_code == 438) {
+ if (ctx->retry_ct > 0) {
+ r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Exceeded the number of retries", ctx->tctx->label);
+ ABORT(R_FAILED);
+ }
+
+ if (!ctx->stun->nonce) {
+ r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no nonce", ctx->tctx->label);
+ ABORT(R_FAILED);
+ }
+ if (!ctx->stun->realm) {
+ r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no realm", ctx->tctx->label);
+ ABORT(R_FAILED);
+ }
+
+ /* Try to retry */
+ if ((r=nr_turn_stun_set_auth_params(ctx, ctx->stun->realm,
+ ctx->stun->nonce)))
+ ABORT(r);
+
+ ctx->stun->error_code = 0; /* Reset to avoid inf-looping */
+
+ if ((r=nr_turn_stun_ctx_start(ctx))) {
+ r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", ctx->tctx->label);
+ ABORT(r);
+ }
+
+ ctx->retry_ct++;
+ }
+ else {
+ ABORT(R_FAILED);
+ }
+ break;
+
+ case NR_STUN_CLIENT_STATE_TIMED_OUT:
+ ABORT(R_FAILED);
+ break;
+
+ case NR_STUN_CLIENT_STATE_CANCELLED:
+ assert(0); /* Shouldn't happen */
+ return;
+ break;
+
+ default:
+ assert(0); /* Shouldn't happen */
+ return;
+ }
+
+ _status=0;
+abort:
+ if (_status) {
+ ctx->error_cb(0, 0, ctx);
+ }
+}
+
+/* nr_turn_client_ctx functions */
+int nr_turn_client_ctx_create(const char *label, nr_socket *sock,
+ const char *username, Data *password,
+ nr_transport_addr *addr,
+ nr_turn_client_ctx **ctxp)
+{
+ nr_turn_client_ctx *ctx=0;
+ int r,_status;
+
+ if ((r=r_log_register("turn", &NR_LOG_TURN)))
+ ABORT(r);
+
+ if(!(ctx=RCALLOC(sizeof(nr_turn_client_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ STAILQ_INIT(&ctx->stun_ctxs);
+ STAILQ_INIT(&ctx->permissions);
+
+ if(!(ctx->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+
+ ctx->sock=sock;
+ ctx->username = r_strdup(username);
+ if (!ctx->username)
+ ABORT(R_NO_MEMORY);
+
+ if ((r=r_data_create(&ctx->password, password->data, password->len)))
+ ABORT(r);
+ if ((r=nr_transport_addr_copy(&ctx->turn_server_addr, addr)))
+ ABORT(r);
+
+ ctx->state = NR_TURN_CLIENT_STATE_INITTED;
+ if (addr->protocol == IPPROTO_TCP) {
+ if ((r=nr_socket_connect(ctx->sock, &ctx->turn_server_addr))) {
+ if (r != R_WOULDBLOCK)
+ ABORT(r);
+ }
+ }
+
+ *ctxp=ctx;
+
+ _status=0;
+abort:
+ if(_status){
+ nr_turn_client_ctx_destroy(&ctx);
+ }
+ return(_status);
+}
+
+int
+nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp)
+{
+ nr_turn_client_ctx *ctx;
+
+ if(!ctxp || !*ctxp)
+ return(0);
+
+ ctx=*ctxp;
+ *ctxp = 0;
+
+ if (ctx->label)
+ r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): destroy", ctx->label);
+
+ nr_turn_client_deallocate(ctx);
+
+ /* Cancel frees the rest of our data */
+ RFREE(ctx->label);
+ ctx->label = 0;
+
+ nr_turn_client_cancel(ctx);
+
+ RFREE(ctx->username);
+ ctx->username = 0;
+ r_data_destroy(&ctx->password);
+ RFREE(ctx->nonce);
+ ctx->nonce = 0;
+ RFREE(ctx->realm);
+ ctx->realm = 0;
+
+ /* Destroy the STUN client ctxs */
+ while (!STAILQ_EMPTY(&ctx->stun_ctxs)) {
+ nr_turn_stun_ctx *stun = STAILQ_FIRST(&ctx->stun_ctxs);
+ STAILQ_REMOVE_HEAD(&ctx->stun_ctxs, entry);
+ nr_turn_stun_ctx_destroy(&stun);
+ }
+
+ /* Destroy the permissions */
+ while (!STAILQ_EMPTY(&ctx->permissions)) {
+ nr_turn_permission *perm = STAILQ_FIRST(&ctx->permissions);
+ STAILQ_REMOVE_HEAD(&ctx->permissions, entry);
+ nr_turn_permission_destroy(&perm);
+ }
+
+ RFREE(ctx);
+
+ return(0);
+}
+
+int nr_turn_client_cancel(nr_turn_client_ctx *ctx)
+{
+ nr_turn_stun_ctx *stun = 0;
+
+ if (ctx->state == NR_TURN_CLIENT_STATE_CANCELLED ||
+ ctx->state == NR_TURN_CLIENT_STATE_FAILED)
+ return(0);
+
+ if (ctx->label)
+ r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): cancelling", ctx->label);
+
+ /* Cancel the STUN client ctxs */
+ stun = STAILQ_FIRST(&ctx->stun_ctxs);
+ while (stun) {
+ nr_stun_client_cancel(stun->stun);
+ stun = STAILQ_NEXT(stun, entry);
+ }
+
+ /* Cancel the timers, if not already cancelled */
+ NR_async_timer_cancel(ctx->connected_timer_handle);
+ NR_async_timer_cancel(ctx->refresh_timer_handle);
+
+ ctx->state = NR_TURN_CLIENT_STATE_CANCELLED;
+
+ return(0);
+}
+
+int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx,
+ nr_stun_message *req,
+ int flags)
+{
+ int r,_status;
+
+ if ((r=nr_stun_encode_message(req)))
+ ABORT(r);
+
+ if ((r=nr_socket_sendto(ctx->sock,
+ req->buffer, req->length, flags,
+ &ctx->turn_server_addr))) {
+ r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Failed sending request",
+ ctx->label);
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_turn_client_deallocate(nr_turn_client_ctx *ctx)
+{
+ int r,_status;
+ nr_stun_message *aloc = 0;
+ nr_stun_client_auth_params auth;
+ nr_stun_client_refresh_request_params refresh;
+
+ if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED)
+ return(0);
+
+ r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): deallocating", ctx->label);
+
+ refresh.lifetime_secs = 0;
+
+ auth.username = ctx->username;
+ INIT_DATA(auth.password, ctx->password->data, ctx->password->len);
+
+ auth.realm = ctx->realm;
+ auth.nonce = ctx->nonce;
+
+ auth.authenticate = 1;
+
+ if ((r=nr_stun_build_refresh_request(&auth, &refresh, &aloc)))
+ ABORT(r);
+
+ // We are only sending a single request here because we are in the process of
+ // shutting everything down. Theoretically we should probably start a seperate
+ // STUN transaction which outlives the TURN context.
+ if ((r=nr_turn_client_send_stun_request(ctx, aloc, 0)))
+ ABORT(r);
+
+ ctx->state = NR_TURN_CLIENT_STATE_DEALLOCATING;
+
+ _status=0;
+abort:
+ nr_stun_message_destroy(&aloc);
+ return(_status);
+}
+
+static void nr_turn_client_fire_finished_cb(nr_turn_client_ctx *ctx)
+ {
+ if (ctx->finished_cb) {
+ NR_async_cb finished_cb=ctx->finished_cb;
+ ctx->finished_cb=0;
+ finished_cb(0, 0, ctx->cb_arg);
+ }
+ }
+
+int nr_turn_client_failed(nr_turn_client_ctx *ctx)
+{
+ if (ctx->state == NR_TURN_CLIENT_STATE_FAILED ||
+ ctx->state == NR_TURN_CLIENT_STATE_CANCELLED)
+ return(0);
+
+ r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s) failed", ctx->label);
+ nr_turn_client_cancel(ctx);
+ ctx->state = NR_TURN_CLIENT_STATE_FAILED;
+ nr_turn_client_fire_finished_cb(ctx);
+
+ return(0);
+}
+
+int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx,
+ nr_transport_addr *relayed_address)
+{
+ int r, _status;
+
+ if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED)
+ ABORT(R_FAILED);
+
+ if (r=nr_transport_addr_copy(relayed_address, &ctx->relay_addr))
+ ABORT(r);
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx,
+ nr_transport_addr *mapped_address)
+{
+ int r, _status;
+
+ if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED)
+ ABORT(R_FAILED);
+
+ if (r=nr_transport_addr_copy(mapped_address, &ctx->mapped_addr))
+ ABORT(r);
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static void nr_turn_client_allocate_cb(NR_SOCKET s, int how, void *arg)
+{
+ nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
+ nr_turn_stun_ctx *refresh_ctx;
+ int r,_status;
+
+ ctx->tctx->state = NR_TURN_CLIENT_STATE_ALLOCATED;
+
+ if ((r=nr_transport_addr_copy(
+ &ctx->tctx->relay_addr,
+ &ctx->stun->results.allocate_response.relay_addr)))
+ ABORT(r);
+
+ if ((r=nr_transport_addr_copy(
+ &ctx->tctx->mapped_addr,
+ &ctx->stun->results.allocate_response.mapped_addr)))
+ ABORT(r);
+
+ if ((r=nr_turn_client_refresh_setup(ctx->tctx, &refresh_ctx)))
+ ABORT(r);
+
+ if ((r=nr_turn_client_start_refresh_timer(
+ ctx->tctx, refresh_ctx,
+ ctx->stun->results.allocate_response.lifetime_secs)))
+ ABORT(r);
+
+ r_log(NR_LOG_TURN, LOG_INFO,
+ "TURN(%s): Succesfully allocated addr %s lifetime=%u",
+ ctx->tctx->label,
+ ctx->tctx->relay_addr.as_string,
+ ctx->stun->results.allocate_response.lifetime_secs);
+
+ nr_turn_client_fire_finished_cb(ctx->tctx);
+ _status=0;
+abort:
+ if (_status) {
+ nr_turn_client_failed(ctx->tctx);
+ }
+}
+
+static void nr_turn_client_error_cb(NR_SOCKET s, int how, void *arg)
+{
+ nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
+
+ r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, %s",
+ ctx->tctx->label, ctx->mode, __FUNCTION__);
+
+ nr_turn_client_failed(ctx->tctx);
+}
+
+static void nr_turn_client_permission_error_cb(NR_SOCKET s, int how, void *arg)
+{
+ nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
+
+ if (ctx->last_error_code == 403) {
+ r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, permission denied",
+ ctx->tctx->label, ctx->mode);
+
+ } else{
+ nr_turn_client_error_cb(0, 0, ctx);
+ }
+}
+
+int nr_turn_client_allocate(nr_turn_client_ctx *ctx,
+ NR_async_cb finished_cb, void *cb_arg)
+{
+ nr_turn_stun_ctx *stun = 0;
+ int r,_status;
+
+ if(ctx->state == NR_TURN_CLIENT_STATE_FAILED ||
+ ctx->state == NR_TURN_CLIENT_STATE_CANCELLED){
+ /* TURN TCP contexts can fail before we ever try to form an allocation,
+ * since the TCP connection can fail. It is also conceivable that a TURN
+ * TCP context could be cancelled before we are done forming all
+ * allocations (although we do not do this at the time this code was
+ * written) */
+ assert(ctx->turn_server_addr.protocol == IPPROTO_TCP);
+ ABORT(R_NOT_FOUND);
+ }
+
+ assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED);
+
+ ctx->finished_cb=finished_cb;
+ ctx->cb_arg=cb_arg;
+
+ if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST,
+ nr_turn_client_allocate_cb,
+ nr_turn_client_error_cb,
+ &stun)))
+ ABORT(r);
+ stun->stun->params.allocate_request.lifetime_secs =
+ TURN_LIFETIME_REQUEST_SECONDS;
+
+ if (ctx->state == NR_TURN_CLIENT_STATE_INITTED) {
+ if ((r=nr_turn_stun_ctx_start(stun)))
+ ABORT(r);
+ ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING;
+ } else {
+ ABORT(R_ALREADY);
+ }
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_turn_client_failed(ctx);
+ }
+
+ return(_status);
+}
+
+int nr_turn_client_process_response(nr_turn_client_ctx *ctx,
+ UCHAR *msg, int len,
+ nr_transport_addr *turn_server_addr)
+{
+ int r, _status;
+ nr_turn_stun_ctx *sc1;
+
+ switch (ctx->state) {
+ case NR_TURN_CLIENT_STATE_ALLOCATING:
+ case NR_TURN_CLIENT_STATE_ALLOCATED:
+ break;
+ default:
+ ABORT(R_FAILED);
+ }
+
+ sc1 = STAILQ_FIRST(&ctx->stun_ctxs);
+ while (sc1) {
+ r = nr_stun_client_process_response(sc1->stun, msg, len, turn_server_addr);
+ if (!r)
+ break;
+ if (r==R_RETRY) /* Likely a 401 and we will retry */
+ break;
+ if (r != R_REJECTED)
+ ABORT(r);
+ sc1 = STAILQ_NEXT(sc1, entry);
+ }
+ if (!sc1)
+ ABORT(R_REJECTED);
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx,
+ nr_turn_stun_ctx **sctx)
+{
+ nr_turn_stun_ctx *stun = 0;
+ int r,_status;
+
+ assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED);
+ if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED)
+ ABORT(R_NOT_PERMITTED);
+
+ if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_REFRESH_REQUEST,
+ nr_turn_client_refresh_cb,
+ nr_turn_client_error_cb,
+ &stun)))
+ ABORT(r);
+
+ if ((r=nr_turn_stun_set_auth_params(stun, ctx->realm, ctx->nonce)))
+ ABORT(r);
+
+ stun->stun->params.refresh_request.lifetime_secs =
+ TURN_LIFETIME_REQUEST_SECONDS;
+
+
+ *sctx=stun;
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *tctx,
+ nr_turn_stun_ctx *sctx,
+ UINT4 lifetime)
+{
+ int _status;
+
+ assert(!tctx->refresh_timer_handle);
+
+ if (lifetime <= TURN_REFRESH_SLACK_SECONDS) {
+ r_log(NR_LOG_TURN, LOG_ERR, "Too short lifetime specified for turn %u", lifetime);
+ ABORT(R_BAD_DATA);
+ }
+
+ if (lifetime > 3600)
+ lifetime = 3600;
+
+ lifetime -= TURN_REFRESH_SLACK_SECONDS;
+
+ r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Setting refresh timer for %u seconds",
+ tctx->label, lifetime);
+ NR_ASYNC_TIMER_SET(lifetime * 1000, nr_turn_client_refresh_timer_cb, sctx,
+ &tctx->refresh_timer_handle);
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_turn_client_failed(tctx);
+ }
+ return _status;
+}
+
+static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg)
+{
+ nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
+ int r,_status;
+
+ r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh timer fired",
+ ctx->tctx->label);
+
+ ctx->tctx->refresh_timer_handle=0;
+ if ((r=nr_turn_stun_ctx_start(ctx))) {
+ ABORT(r);
+ }
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_turn_client_failed(ctx->tctx);
+ }
+ return;
+}
+
+static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg)
+{
+ int r, _status;
+ nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
+ /* Save lifetime from the reset */
+ UINT4 lifetime = ctx->stun->results.refresh_response.lifetime_secs;
+
+ r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh succeeded. lifetime=%u",
+ ctx->tctx->label, lifetime);
+
+ if ((r=nr_turn_client_start_refresh_timer(
+ ctx->tctx, ctx, lifetime)))
+ ABORT(r);
+
+ _status=0;
+
+abort:
+ if (_status) {
+ nr_turn_client_failed(ctx->tctx);
+ }
+}
+
+/* TODO(ekr@rtfm.com): We currently don't support channels.
+ We might in the future. Mozilla bug 857736 */
+int nr_turn_client_send_indication(nr_turn_client_ctx *ctx,
+ const UCHAR *msg, size_t len,
+ int flags, nr_transport_addr *remote_addr)
+{
+ int r,_status;
+ nr_stun_client_send_indication_params params = { { 0 } };
+ nr_stun_message *ind = 0;
+
+ if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED)
+ ABORT(R_FAILED);
+
+ r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Send indication len=%zu",
+ ctx->label, len);
+
+ if ((r=nr_turn_client_ensure_perm(ctx, remote_addr)))
+ ABORT(r);
+
+ if ((r=nr_transport_addr_copy(&params.remote_addr, remote_addr)))
+ ABORT(r);
+
+ params.data.data = (UCHAR*)msg;
+ params.data.len = len;
+
+ if ((r=nr_stun_build_send_indication(&params, &ind)))
+ ABORT(r);
+
+ if ((r=nr_turn_client_send_stun_request(ctx, ind, flags)))
+ ABORT(r);
+
+ _status=0;
+abort:
+ nr_stun_message_destroy(&ind);
+ return(_status);
+}
+
+int nr_turn_client_parse_data_indication(nr_turn_client_ctx *ctx,
+ nr_transport_addr *source_addr,
+ UCHAR *msg, size_t len,
+ UCHAR *newmsg, size_t *newlen,
+ size_t newsize,
+ nr_transport_addr *remote_addr)
+{
+ int r,_status;
+ nr_stun_message *ind=0;
+ nr_stun_message_attribute *attr;
+ nr_turn_permission *perm;
+
+ if (nr_transport_addr_cmp(&ctx->turn_server_addr, source_addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ r_log(NR_LOG_TURN, LOG_WARNING,
+ "TURN(%s): Indication from unexpected source addr %s (expected %s)",
+ ctx->label, source_addr->as_string, ctx->turn_server_addr.as_string);
+ ABORT(R_REJECTED);
+ }
+
+ if ((r=nr_stun_message_create2(&ind, msg, len)))
+ ABORT(r);
+ if ((r=nr_stun_decode_message(ind, 0, 0)))
+ ABORT(r);
+
+ if (ind->header.type != NR_STUN_MSG_DATA_INDICATION)
+ ABORT(R_BAD_ARGS);
+
+ if (!nr_stun_message_has_attribute(ind, NR_STUN_ATTR_XOR_PEER_ADDRESS, &attr))
+ ABORT(R_BAD_ARGS);
+
+ if ((r=nr_turn_permission_find(ctx, &attr->u.xor_mapped_address.unmasked,
+ &perm))) {
+ if (r == R_NOT_FOUND) {
+ r_log(NR_LOG_TURN, LOG_WARNING,
+ "TURN(%s): Indication from peer addr %s with no permission",
+ ctx->label, attr->u.xor_mapped_address.unmasked.as_string);
+ }
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_copy(remote_addr,
+ &attr->u.xor_mapped_address.unmasked)))
+ ABORT(r);
+
+#if REFRESH_RESERVATION_ON_RECV
+ if ((r=nr_turn_client_ensure_perm(ctx, remote_addr))) {
+ ABORT(r);
+ }
+#endif
+
+ if (!nr_stun_message_has_attribute(ind, NR_STUN_ATTR_DATA, &attr)) {
+ ABORT(R_BAD_DATA);
+ }
+
+ assert(newsize >= attr->u.data.length);
+ if (newsize < attr->u.data.length)
+ ABORT(R_BAD_ARGS);
+
+ memcpy(newmsg, attr->u.data.data, attr->u.data.length);
+ *newlen = attr->u.data.length;
+
+ _status=0;
+abort:
+ nr_stun_message_destroy(&ind);
+ return(_status);
+}
+
+
+
+/* The permissions model is as follows:
+
+ - We keep a list of all the permissions we have ever requested
+ along with when they were last established.
+ - Whenever someone sends a packet, we automatically create/
+ refresh the permission.
+
+ This means that permissions automatically time out if
+ unused.
+
+*/
+int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx, nr_transport_addr *addr)
+{
+ int r, _status;
+ nr_turn_permission *perm = 0;
+ UINT8 now;
+ UINT8 turn_permission_refresh = (TURN_PERMISSION_LIFETIME_SECONDS -
+ TURN_REFRESH_SLACK_SECONDS) * TURN_USECS_PER_S;
+
+ if ((r=nr_turn_permission_find(ctx, addr, &perm))) {
+ if (r == R_NOT_FOUND) {
+ if ((r=nr_turn_permission_create(ctx, addr, &perm)))
+ ABORT(r);
+ }
+ else {
+ ABORT(r);
+ }
+ }
+
+ assert(perm);
+
+ /* Now check that the permission is up-to-date */
+ now = r_gettimeint();
+
+ if ((now - perm->last_used) > turn_permission_refresh) {
+ r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Permission for %s requires refresh",
+ ctx->label, perm->addr.as_string);
+
+ if ((r=nr_turn_stun_ctx_start(perm->stun)))
+ ABORT(r);
+
+ perm->last_used = now; /* Update the time now so we don't retry on
+ next packet */
+ }
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static int nr_turn_permission_create(nr_turn_client_ctx *ctx, nr_transport_addr *addr,
+ nr_turn_permission **permp)
+{
+ int r, _status;
+ nr_turn_permission *perm = 0;
+
+ assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED);
+
+ r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): Creating permission for %s",
+ ctx->label, addr->as_string);
+
+ if (!(perm = RCALLOC(sizeof(nr_turn_permission))))
+ ABORT(R_NO_MEMORY);
+
+ if ((r=nr_transport_addr_copy(&perm->addr, addr)))
+ ABORT(r);
+
+ perm->last_used = 0;
+
+ if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_PERMISSION_REQUEST,
+ nr_turn_client_permissions_cb,
+ nr_turn_client_permission_error_cb,
+ &perm->stun)))
+ ABORT(r);
+
+ /* We want to authenticate on the first packet */
+ if ((r=nr_turn_stun_set_auth_params(perm->stun, ctx->realm, ctx->nonce)))
+ ABORT(r);
+
+ if ((r=nr_transport_addr_copy(
+ &perm->stun->stun->params.permission_request.remote_addr, addr)))
+ ABORT(r);
+ STAILQ_INSERT_TAIL(&ctx->permissions, perm, entry);
+
+ *permp = perm;
+
+ _status=0;
+abort:
+ if (_status) {
+ nr_turn_permission_destroy(&perm);
+ }
+ return(_status);
+}
+
+
+static int nr_turn_permission_find(nr_turn_client_ctx *ctx, nr_transport_addr *addr,
+ nr_turn_permission **permp)
+{
+ nr_turn_permission *perm;
+ int _status;
+
+ perm = STAILQ_FIRST(&ctx->permissions);
+ while (perm) {
+ if (!nr_transport_addr_cmp(&perm->addr, addr,
+ NR_TRANSPORT_ADDR_CMP_MODE_ADDR))
+ break;
+
+ perm = STAILQ_NEXT(perm, entry);
+ }
+
+ if (!perm) {
+ ABORT(R_NOT_FOUND);
+ }
+ if (perm->stun->last_error_code == 403) {
+ ABORT(R_NOT_PERMITTED);
+ }
+ *permp = perm;
+
+ _status=0;
+abort:
+ return(_status);
+}
+
+static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *arg)
+{
+ nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg;
+ r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Successfully refreshed permission",
+ ctx->tctx->label);
+}
+
+/* Note that we don't destroy the nr_turn_stun_ctx. That is owned by the
+ nr_turn_client_ctx. */
+static int nr_turn_permission_destroy(nr_turn_permission **permp)
+{
+ nr_turn_permission *perm;
+
+ if (!permp || !*permp)
+ return(0);
+
+ perm = *permp;
+ *permp = 0;
+
+ RFREE(perm);
+
+ return(0);
+}
+
+#endif /* USE_TURN */
diff --git a/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h
new file mode 100644
index 000000000..c50e79d3b
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h
@@ -0,0 +1,137 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+Copyright (c) 2013, Mozilla
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance, Mozilla nor
+ the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _turn_client_ctx_h
+#define _turn_client_ctx_h
+
+/*
+ Represents a single set of STUN transactions, i.e.,
+ Allocate, Refresh, Permission. It automatically handles
+ permissions and restarts.
+ */
+typedef struct nr_turn_stun_ctx_ {
+ struct nr_turn_client_ctx_ *tctx;
+ int mode; /* From stun_client_ctx.h, NR_TURN_CLIENT_MODE_* */
+ int retry_ct;
+ nr_stun_client_ctx *stun;
+ char *nonce;
+ char *realm;
+ NR_async_cb success_cb;
+ NR_async_cb error_cb;
+ int last_error_code;
+
+ STAILQ_ENTRY(nr_turn_stun_ctx_) entry;
+} nr_turn_stun_ctx;
+typedef STAILQ_HEAD(nr_turn_stun_ctx_head_, nr_turn_stun_ctx_)
+ nr_turn_stun_ctx_head;
+
+/* Represents a single TURN permission */
+typedef struct nr_turn_permission_ {
+ nr_transport_addr addr;
+ nr_turn_stun_ctx *stun;
+ UINT8 last_used;
+
+ STAILQ_ENTRY(nr_turn_permission_) entry;
+} nr_turn_permission;
+typedef STAILQ_HEAD(nr_turn_permission_head_, nr_turn_permission_)
+ nr_turn_permission_head;
+
+/* A single connection to a TURN server. Use one
+ turn_client_ctx per socket/server pair. */
+typedef struct nr_turn_client_ctx_ {
+ int state;
+#define NR_TURN_CLIENT_STATE_INITTED 1
+#define NR_TURN_CLIENT_STATE_ALLOCATING 2
+#define NR_TURN_CLIENT_STATE_ALLOCATED 3
+#define NR_TURN_CLIENT_STATE_FAILED 4
+#define NR_TURN_CLIENT_STATE_CANCELLED 5
+#define NR_TURN_CLIENT_STATE_DEALLOCATING 6
+
+ char *label;
+ nr_socket *sock;
+
+ char *username;
+ Data *password;
+ char *nonce;
+ char *realm;
+
+ nr_transport_addr turn_server_addr;
+ nr_transport_addr relay_addr;
+ nr_transport_addr mapped_addr;
+
+ nr_turn_stun_ctx_head stun_ctxs;
+ nr_turn_permission_head permissions;
+
+ NR_async_cb finished_cb;
+ void *cb_arg;
+
+ void *connected_timer_handle;
+ void *refresh_timer_handle;
+} nr_turn_client_ctx;
+
+extern int NR_LOG_TURN;
+
+int nr_turn_client_ctx_create(const char *label, nr_socket *sock,
+ const char *username, Data *password,
+ nr_transport_addr *addr,
+ nr_turn_client_ctx **ctxp);
+int nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp);
+int nr_turn_client_allocate(nr_turn_client_ctx *ctx,
+ NR_async_cb finished_cb, void *cb_arg);
+int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx,
+ nr_transport_addr *relayed_address);
+int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx,
+ nr_transport_addr *mapped_address);
+int nr_turn_client_process_response(nr_turn_client_ctx *ctx,
+ UCHAR *msg, int len,
+ nr_transport_addr *turn_server_addr);
+int nr_turn_client_cancel(nr_turn_client_ctx *ctx);
+int nr_turn_client_failed(nr_turn_client_ctx *ctx);
+int nr_turn_client_deallocate(nr_turn_client_ctx *ctx);
+int nr_turn_client_send_indication(nr_turn_client_ctx *ctx,
+ const UCHAR *msg, size_t len,
+ int flags, nr_transport_addr *remote_addr);
+int nr_turn_client_parse_data_indication(nr_turn_client_ctx *ctx,
+ nr_transport_addr *source_addr,
+ UCHAR *msg, size_t len,
+ UCHAR *newmsg, size_t *newlen,
+ size_t newsize,
+ nr_transport_addr *remote_addr);
+int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx,
+ nr_transport_addr *addr);
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/util/cb_args.c b/media/mtransport/third_party/nICEr/src/util/cb_args.c
new file mode 100644
index 000000000..964f1abee
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/util/cb_args.c
@@ -0,0 +1,61 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+static char *RCSSTRING __UNUSED__="$Id: cb_args.c,v 1.2 2008/04/28 17:59:04 ekr Exp $";
+
+#include <stdarg.h>
+#include "nr_api.h"
+#include "cb_args.h"
+
+void **nr_pack_cb_args(int ct,...)
+ {
+ void **vlist;
+ va_list ap;
+ int i;
+
+ va_start(ap,ct);
+ if(!(vlist=RCALLOC(sizeof(void *)*ct+1)))
+ abort();
+
+ for(i=0;i<ct;i++){
+ vlist[i]=va_arg(ap, void *);
+ }
+
+ va_end(ap);
+
+ return(vlist);
+ }
+
+
+
diff --git a/media/mtransport/third_party/nICEr/src/util/cb_args.h b/media/mtransport/third_party/nICEr/src/util/cb_args.h
new file mode 100644
index 000000000..83f7831cc
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/util/cb_args.h
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _cb_args_h
+#define _cb_args_h
+
+void **nr_pack_cb_args(int ct,...);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/util/ice_util.c b/media/mtransport/third_party/nICEr/src/util/ice_util.c
new file mode 100644
index 000000000..047be6659
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/util/ice_util.c
@@ -0,0 +1,76 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdarg.h>
+
+
+static char *RCSSTRING __UNUSED__="$Id: ice_util.c,v 1.2 2008/04/28 17:59:05 ekr Exp $";
+
+#include <stdarg.h>
+#include <string.h>
+#include "nr_api.h"
+#include "ice_util.h"
+
+int nr_concat_strings(char **outp,...)
+ {
+ va_list ap;
+ char *s,*out=0;
+ int len=0;
+ int _status;
+
+ va_start(ap,outp);
+ while(s=va_arg(ap,char *)){
+ len+=strlen(s);
+ }
+ va_end(ap);
+
+
+ if(!(out=RMALLOC(len+1)))
+ ABORT(R_NO_MEMORY);
+
+ *outp=out;
+
+ va_start(ap,outp);
+ while(s=va_arg(ap,char *)){
+ len=strlen(s);
+ memcpy(out,s,len);
+ out+=len;
+ }
+ va_end(ap);
+
+ *out=0;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
diff --git a/media/mtransport/third_party/nICEr/src/util/ice_util.h b/media/mtransport/third_party/nICEr/src/util/ice_util.h
new file mode 100644
index 000000000..44751edaf
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/util/ice_util.h
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _ice_util_h
+#define _ice_util_h
+
+int nr_concat_strings(char **outp,...);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/src/util/mbslen.c b/media/mtransport/third_party/nICEr/src/util/mbslen.c
new file mode 100644
index 000000000..3645c95cf
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/util/mbslen.c
@@ -0,0 +1,139 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifdef LINUX
+#define _GNU_SOURCE 1
+#endif
+#include <string.h>
+
+#include <errno.h>
+#include <csi_platform.h>
+
+#include <assert.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <wchar.h>
+#if defined(DARWIN) || defined(__DragonFly__) || defined(__FreeBSD__)
+#define HAVE_XLOCALE
+#endif
+
+#ifdef HAVE_XLOCALE
+#include <xlocale.h>
+#endif /* HAVE_XLOCALE */
+
+#include "nr_api.h"
+#include "mbslen.h"
+
+/* get number of characters in a mult-byte character string */
+int
+mbslen(const char *s, size_t *ncharsp)
+{
+#ifdef HAVE_XLOCALE
+ static locale_t loc = 0;
+ static int initialized = 0;
+#endif /* HAVE_XLOCALE */
+#ifdef WIN32
+ char *my_locale=0;
+ unsigned int i;
+#endif /* WIN32 */
+ int _status;
+ size_t nbytes;
+ int nchars;
+ mbstate_t mbs;
+
+#ifdef HAVE_XLOCALE
+ if (! initialized) {
+ initialized = 1;
+ loc = newlocale(LC_CTYPE_MASK, "UTF-8", LC_GLOBAL_LOCALE);
+ }
+
+ if (loc == 0) {
+ /* unable to create the UTF-8 locale */
+ assert(loc != 0); /* should never happen */
+#endif /* HAVE_XLOCALE */
+
+#ifdef WIN32
+ if (!setlocale(LC_CTYPE, 0))
+ ABORT(R_INTERNAL);
+
+ if (!(my_locale = r_strdup(setlocale(LC_CTYPE, 0))))
+ ABORT(R_NO_MEMORY);
+
+ for (i=0; i<strlen(my_locale); i++)
+ my_locale[i] = toupper(my_locale[i]);
+
+ if (!strstr(my_locale, "UTF-8") && !strstr(my_locale, "UTF8"))
+ ABORT(R_NOT_FOUND);
+#else
+ /* can't count UTF-8 characters with mbrlen if the locale isn't UTF-8 */
+ /* null-checking setlocale is required because Android */
+ char *locale = setlocale(LC_CTYPE, 0);
+ /* some systems use "utf8" instead of "UTF-8" like Fedora 17 */
+ if (!locale || (!strcasestr(locale, "UTF-8") && !strcasestr(locale, "UTF8")))
+ ABORT(R_NOT_FOUND);
+#endif
+
+#ifdef HAVE_XLOCALE
+ }
+#endif /* HAVE_XLOCALE */
+
+ memset(&mbs, 0, sizeof(mbs));
+ nchars = 0;
+
+#ifdef HAVE_XLOCALE
+ while (*s != '\0' && (nbytes = mbrlen_l(s, strlen(s), &mbs, loc)) != 0)
+#else
+ while (*s != '\0' && (nbytes = mbrlen(s, strlen(s), &mbs)) != 0)
+#endif /* HAVE_XLOCALE */
+ {
+ if (nbytes == (size_t)-1) /* should never happen */ {
+ ABORT(R_INTERNAL);
+ }
+ if (nbytes == (size_t)-2) /* encoding error */ {
+ ABORT(R_BAD_DATA);
+ }
+
+ s += nbytes;
+ ++nchars;
+ }
+
+ *ncharsp = nchars;
+
+ _status = 0;
+ abort:
+#ifdef WIN32
+ RFREE(my_locale);
+#endif
+ return _status;
+}
+
diff --git a/media/mtransport/third_party/nICEr/src/util/mbslen.h b/media/mtransport/third_party/nICEr/src/util/mbslen.h
new file mode 100644
index 000000000..9a68e5dae
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/src/util/mbslen.h
@@ -0,0 +1,42 @@
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _mbslen_h
+#define _mbslen_h
+
+/* get number of characters in a mult-byte character string */
+int mbslen(const char *s, size_t *ncharsp);
+
+#endif
+
diff --git a/media/mtransport/third_party/nICEr/upstream.diff b/media/mtransport/third_party/nICEr/upstream.diff
new file mode 100644
index 000000000..a664fd9d2
--- /dev/null
+++ b/media/mtransport/third_party/nICEr/upstream.diff
@@ -0,0 +1,2449 @@
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.c src/ice/ice_candidate.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_candidate.c 2012-10-06 08:30:22.000000000 -0700
+@@ -54,36 +54,38 @@
+
+ #include "stun_client_ctx.h"
+ #include "stun_server_ctx.h"
+ #include "turn_client_ctx.h"
+ #include "ice_ctx.h"
+ #include "ice_candidate.h"
+ #include "ice_reg.h"
+ #include "ice_util.h"
+ #include "nr_socket_turn.h"
+
++static int next_automatic_preference = 224;
++
+ static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand);
+ static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg);
+-static void nr_ice_srvrflx_stun_finished_cb(int sock, int how, void *cb_arg);
++static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg);
+ #ifdef USE_TURN
+ static int nr_ice_start_relay_turn(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg);
+-static void nr_ice_turn_allocated_cb(int sock, int how, void *cb_arg);
++static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg);
+ #endif /* USE_TURN */
+
+ char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0};
+
+ int nr_ice_candidate_create(nr_ice_ctx *ctx,char *label,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp)
+ {
+ nr_ice_candidate *cand=0;
+ nr_ice_candidate *tmp=0;
+ int r,_status;
+-
++
+ if(!(cand=RCALLOC(sizeof(nr_ice_candidate))))
+ ABORT(R_NO_MEMORY);
+ if(!(cand->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+ cand->state=NR_ICE_CAND_STATE_CREATED;
+ cand->ctx=ctx;
+ cand->isock=isock;
+ cand->osock=osock;
+ cand->type=ctype;
+ cand->stun_server=stun_server;
+@@ -189,21 +191,21 @@
+ if(cand->delay_timer)
+ NR_async_timer_cancel(cand->delay_timer);
+
+ RFREE(cand->foundation);
+ RFREE(cand->label);
+ RFREE(cand);
+
+ return(0);
+ }
+
+-void nr_ice_candidate_destroy_cb(int s, int h, void *cb_arg)
++void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg)
+ {
+ nr_ice_candidate *cand=cb_arg;
+ nr_ice_candidate_destroy(&cand);
+ }
+
+ /* This algorithm is not super-fast, but I don't think we need a hash
+ table just yet and it produces a small foundation string */
+ static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand)
+ {
+ nr_ice_foundation *foundation;
+@@ -276,22 +278,38 @@
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+
+ if(type_preference > 126)
+ r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference);
+
+
+ if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,
+- &interface_preference))
+- ABORT(r);
++ &interface_preference)) {
++ if (r==R_NOT_FOUND) {
++ if (next_automatic_preference == 1) {
++ r_log(LOG_ICE,LOG_DEBUG,"Out of preference values. Can't assign one for interface %s",cand->base.ifname);
++ ABORT(R_NOT_FOUND);
++ }
++ r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname,
++ next_automatic_preference);
++ if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){
++ ABORT(r);
++ }
++ interface_preference=next_automatic_preference;
++ next_automatic_preference--;
++ }
++ else {
++ ABORT(r);
++ }
++ }
+
+ cand->priority=
+ (type_preference << 24) |
+ (interface_preference << 16) |
+ (stun_priority << 8) |
+ (256 - cand->component_id);
+
+ /* S 4.1.2 */
+ assert(cand->priority>=1&&cand->priority<=2147483647);
+
+@@ -306,21 +324,22 @@
+
+ cand->done_cb=ready_cb;
+ cand->cb_arg=cb_arg;
+
+ switch(cand->type){
+ case HOST:
+ if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr))
+ ABORT(r);
+ cand->osock=cand->isock->sock;
+ cand->state=NR_ICE_CAND_STATE_INITIALIZED;
+- ready_cb(0,0,cb_arg);
++ // Post this so that it doesn't happen in-line
++ NR_ASYNC_SCHEDULE(ready_cb,cb_arg);
+ break;
+ #ifdef USE_TURN
+ case RELAYED:
+ if(r=nr_ice_start_relay_turn(cand,ready_cb,cb_arg))
+ ABORT(r);
+ ABORT(R_WOULDBLOCK);
+ break;
+ #endif /* USE_TURN */
+ case SERVER_REFLEXIVE:
+ /* Need to start stun */
+@@ -333,21 +352,21 @@
+ ABORT(R_INTERNAL);
+ }
+
+ _status=0;
+ abort:
+ if(_status && _status!=R_WOULDBLOCK)
+ cand->state=NR_ICE_CAND_STATE_FAILED;
+ return(_status);
+ }
+
+-static void nr_ice_srvrflx_start_stun_timer_cb(int s, int how, void *cb_arg)
++static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_candidate *cand=cb_arg;
+ int r,_status;
+
+ cand->delay_timer=0;
+
+ /* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart
+ * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the
+ * TODO: server may not have understood the 0.96-style request */
+ if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96, nr_ice_srvrflx_stun_finished_cb, cand))
+@@ -387,21 +406,21 @@
+
+ _status=0;
+ abort:
+ if(_status){
+ cand->state=NR_ICE_CAND_STATE_FAILED;
+ }
+ return(_status);
+ }
+
+ #ifdef USE_TURN
+-static void nr_ice_start_relay_turn_timer_cb(int s, int how, void *cb_arg)
++static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_candidate *cand=cb_arg;
+ int r,_status;
+ int i;
+
+ cand->delay_timer=0;
+
+ if(r=nr_turn_client_allocate(cand->u.relayed.turn, cand->u.relayed.server->username, cand->u.relayed.server->password, cand->u.relayed.server->bandwidth_kbps, cand->u.relayed.server->lifetime_secs, nr_ice_turn_allocated_cb, cand))
+ ABORT(r);
+
+@@ -443,21 +462,21 @@
+
+ _status=0;
+ abort:
+ if(_status){
+ cand->state=NR_ICE_CAND_STATE_FAILED;
+ }
+ return(_status);
+ }
+ #endif /* USE_TURN */
+
+-static void nr_ice_srvrflx_stun_finished_cb(int sock, int how, void *cb_arg)
++static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg)
+ {
+ int _status;
+ nr_ice_candidate *cand=cb_arg;
+
+ /* Deregister to suppress duplicates */
+ if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */
+ nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle);
+ cand->u.srvrflx.stun_handle=0;
+ }
+
+@@ -481,40 +500,40 @@
+ }
+ _status = 0;
+ abort:
+ if(_status){
+ cand->state=NR_ICE_CAND_STATE_FAILED;
+ cand->done_cb(0,0,cand->cb_arg);
+ }
+ }
+
+ #ifdef USE_TURN
+-static void nr_ice_turn_allocated_cb(int s, int how, void *cb_arg)
++static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ int r,_status;
+ nr_ice_candidate *cand=cb_arg;
+ nr_turn_client_ctx *turn=cand->u.relayed.turn;
+ int i;
+ char *label;
+
+ /* Deregister to suppress duplicates */
+ if(cand->u.relayed.turn_handle){ /* This test because we might have failed before CB registered */
+ nr_ice_socket_deregister(cand->isock,cand->u.relayed.turn_handle);
+ cand->u.relayed.turn_handle=0;
+ }
+
+ switch(turn->state){
+ /* OK, we should have a mapped address */
+ case NR_TURN_CLIENT_STATE_ALLOCATED:
+ /* switch candidate from TURN mode to STUN mode */
+
+- if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",turn->relay_addr.as_string,")",0))
++ if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",turn->relay_addr.as_string,")",NULL))
+ ABORT(r);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Switching from TURN (%s) to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label);
+
+ /* Copy out mapped address and relay address */
+ nr_transport_addr_copy(&turn->relay_addr, &cand->u.relayed.turn->stun_ctx[NR_TURN_CLIENT_PHASE_ALLOCATE_REQUEST2]->results.allocate_response2.relay_addr);
+ nr_transport_addr_copy(&cand->addr, &turn->relay_addr);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-CANDIDATE(%s): base=%s, candidate=%s", cand->label, cand->base.as_string, cand->addr.as_string);
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.h src/ice/ice_candidate.h
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.h 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_candidate.h 2012-10-06 08:30:22.000000000 -0700
+@@ -41,21 +41,22 @@
+
+ typedef enum {HOST=1, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED} nr_ice_candidate_type;
+
+ struct nr_ice_candidate_ {
+ char *label;
+ int state;
+ #define NR_ICE_CAND_STATE_CREATED 1
+ #define NR_ICE_CAND_STATE_INITIALIZING 2
+ #define NR_ICE_CAND_STATE_INITIALIZED 3
+ #define NR_ICE_CAND_STATE_FAILED 4
+-#define NR_ICE_CAND_PEER_CANDIDATE 10
++#define NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED 9
++#define NR_ICE_CAND_PEER_CANDIDATE_PAIRED 10
+ struct nr_ice_ctx_ *ctx;
+ nr_ice_socket *isock; /* The socket to read from
+ (it contains all other candidates
+ on this socket) */
+ nr_socket *osock; /* The socket to write to */
+ nr_ice_media_stream *stream; /* The media stream this is associated with */
+ nr_ice_component *component; /* The component this is associated with */
+ nr_ice_candidate_type type; /* The type of the candidate (S 4.1.1) */
+ UCHAR component_id; /* The component id (S 4.1.2.1) */
+ nr_transport_addr addr; /* The advertised address;
+@@ -89,21 +90,21 @@
+ TAILQ_ENTRY(nr_ice_candidate_) entry_comp;
+ };
+
+ extern char *nr_ice_candidate_type_names[];
+
+
+ int nr_ice_candidate_create(struct nr_ice_ctx_ *ctx,char *label, nr_ice_component *component, nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp);
+ int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg);
+ int nr_ice_candidate_process_stun(nr_ice_candidate *cand, UCHAR *msg, int len, nr_transport_addr *faddr);
+ int nr_ice_candidate_destroy(nr_ice_candidate **candp);
+-void nr_ice_candidate_destroy_cb(int s, int h, void *cb_arg);
++void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg);
+ int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen);
+ int nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *attr,nr_ice_media_stream *stream,nr_ice_candidate **candp);
+ int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp);
+ int nr_ice_candidate_compute_priority(nr_ice_candidate *cand);
+
+ #ifdef __cplusplus
+ }
+ #endif /* __cplusplus */
+ #endif
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.c src/ice/ice_candidate_pair.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_candidate_pair.c 2012-10-06 08:30:22.000000000 -0700
+@@ -37,21 +37,21 @@
+ #include <assert.h>
+ #include <string.h>
+ #include <nr_api.h>
+ #include "ice_ctx.h"
+ #include "ice_util.h"
+ #include "ice_codeword.h"
+ #include "stun.h"
+
+ static char *nr_ice_cand_pair_states[]={"UNKNOWN","FROZEN","WAITING","IN_PROGRESS","FAILED","SUCCEEDED","CANCELLED"};
+
+-static void nr_ice_candidate_pair_restart_stun_controlled_cb(int s, int how, void *cb_arg);
++static void nr_ice_candidate_pair_restart_stun_controlled_cb(NR_SOCKET s, int how, void *cb_arg);
+ static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair,
+ nr_ice_candidate *lcand, nr_ice_candidate *rcand);
+
+ int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp)
+ {
+ nr_ice_cand_pair *pair=0;
+ UINT8 o_priority, a_priority;
+ char *lufrag,*rufrag;
+ char *lpwd,*rpwd;
+ char *l2ruser=0,*r2lpass=0;
+@@ -61,21 +61,21 @@
+ UINT8 t_priority;
+
+ if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair))))
+ ABORT(R_NO_MEMORY);
+
+ pair->pctx=pctx;
+
+ nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand);
+
+ if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|",
+- rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",0))
++ rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL))
+ ABORT(r);
+
+ nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
+ pair->local=lcand;
+ pair->remote=rcand;
+
+ /* Priority computation S 5.7.2 */
+ if(pctx->ctx->flags & NR_ICE_CTX_FLAGS_OFFERER)
+ {
+ assert(!(pctx->ctx->flags & NR_ICE_CTX_FLAGS_ANSWERER));
+@@ -87,21 +87,21 @@
+ o_priority=rcand->priority;
+ a_priority=lcand->priority;
+ }
+ pair->priority=(MIN(o_priority, a_priority))<<32 |
+ (MAX(o_priority, a_priority))<<1 | (o_priority > a_priority?0:1);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx) codeword=%s",pctx->ctx->label,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority,pair->codeword);
+
+ /* Foundation */
+ if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|",
+- rcand->foundation,0))
++ rcand->foundation,NULL))
+ ABORT(r);
+
+
+ /* OK, now the STUN data */
+ lufrag=lcand->stream->ufrag?lcand->stream->ufrag:pctx->ctx->ufrag;
+ lpwd=lcand->stream->pwd?lcand->stream->pwd:pctx->ctx->pwd;
+ rufrag=rcand->stream->ufrag?rcand->stream->ufrag:pctx->peer_ufrag;
+ rpwd=rcand->stream->pwd?rcand->stream->pwd:pctx->peer_pwd;
+
+
+@@ -110,39 +110,39 @@
+
+ /* Make a bogus candidate to compute a theoretical peer reflexive
+ * priority per S 7.1.1.1 */
+ memcpy(&tmpcand, lcand, sizeof(tmpcand));
+ tmpcand.type = PEER_REFLEXIVE;
+ if (r=nr_ice_candidate_compute_priority(&tmpcand))
+ ABORT(r);
+ t_priority = tmpcand.priority;
+
+ /* Our sending context */
+- if(r=nr_concat_strings(&l2ruser,lufrag,":",rufrag,0))
++ if(r=nr_concat_strings(&l2ruser,lufrag,":",rufrag,NULL))
+ ABORT(r);
+ if(r=nr_stun_client_ctx_create(pair->as_string,
+ lcand->osock,
+ &rcand->addr,RTO,&pair->stun_client))
+ ABORT(r);
+ if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(l2ruser)))
+ ABORT(R_NO_MEMORY);
+ if(r=r_data_make(&pair->stun_client->params.ice_binding_request.password,(UCHAR *)lpwd,strlen(lpwd)))
+ ABORT(r);
+ pair->stun_client->params.ice_binding_request.priority=t_priority;
+ pair->stun_client->params.ice_binding_request.control = pctx->controlling?
+ NR_ICE_CONTROLLING:NR_ICE_CONTROLLED;
+
+ pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker;
+
+ /* Our receiving username/passwords. Stash these for later
+ injection into the stun server ctx*/
+- if(r=nr_concat_strings(&pair->r2l_user,rufrag,":",lufrag,0))
++ if(r=nr_concat_strings(&pair->r2l_user,rufrag,":",lufrag,NULL))
+ ABORT(r);
+ if(!(r2lpass=r_strdup(rpwd)))
+ ABORT(R_NO_MEMORY);
+ INIT_DATA(pair->r2l_pwd,(UCHAR *)r2lpass,strlen(r2lpass));
+
+ *pairp=pair;
+
+ _status=0;
+ abort:
+ RFREE(l2ruser);
+@@ -178,21 +178,21 @@
+
+ int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair)
+ {
+ assert(pair->state==NR_ICE_PAIR_STATE_FROZEN);
+
+ nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING);
+
+ return(0);
+ }
+
+-static void nr_ice_candidate_pair_stun_cb(int s, int how, void *cb_arg)
++static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ int r,_status;
+ nr_ice_cand_pair *pair=cb_arg,*orig_pair;
+ nr_ice_candidate *cand=0;
+ nr_stun_message *sres;
+ nr_transport_addr *request_src;
+ nr_transport_addr *request_dst;
+ nr_transport_addr *response_src;
+ nr_transport_addr response_dst;
+ nr_stun_message_attribute *attr;
+@@ -457,32 +457,47 @@
+ abort:
+ return(_status);
+ }
+
+ int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state)
+ {
+ int r,_status;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): setting pair %s to %s",
+ pctx->label,pair->as_string,nr_ice_cand_pair_states[state]);
+- pair->state=state;
+
+- if(pctx->state!=NR_ICE_PAIR_STATE_WAITING){
++ /* NOTE: This function used to reference pctx->state instead of
++ pair->state and the assignment to pair->state was at the top
++ of this function. Because pctx->state was never changed, this seems to have
++ been a typo. The natural logic is "if the state changed
++ decrement the counter" so this implies we should be checking
++ the pair state rather than the pctx->state.
++
++ This didn't cause big problems because waiting_pairs was only
++ used for pacing, so the pacing just was kind of broken.
++
++ This note is here as a reminder until we do more testing
++ and make sure that in fact this was a typo.
++ */
++ if(pair->state!=NR_ICE_PAIR_STATE_WAITING){
+ if(state==NR_ICE_PAIR_STATE_WAITING)
+ pctx->waiting_pairs++;
+ }
+ else{
+ if(state!=NR_ICE_PAIR_STATE_WAITING)
+ pctx->waiting_pairs--;
+
+ assert(pctx->waiting_pairs>=0);
+ }
++ pair->state=state;
++
++
+ if(pair->state==NR_ICE_PAIR_STATE_FAILED){
+ if(r=nr_ice_component_failed_pair(pair->remote->component, pair))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+@@ -505,42 +520,42 @@
+ break;
+ }
+
+ c1=TAILQ_NEXT(c1,entry);
+ }
+ if(!c1) TAILQ_INSERT_TAIL(head,pair,entry);
+
+ return(0);
+ }
+
+-void nr_ice_candidate_pair_restart_stun_nominated_cb(int s, int how, void *cb_arg)
++void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_cand_pair *pair=cb_arg;
+ int r,_status;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as nominated",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string);
+
+ nr_stun_client_reset(pair->stun_client);
+ pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLING;
+
+ if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair))
+ ABORT(r);
+
+ if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return;
+ }
+
+-static void nr_ice_candidate_pair_restart_stun_controlled_cb(int s, int how, void *cb_arg)
++static void nr_ice_candidate_pair_restart_stun_controlled_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_cand_pair *pair=cb_arg;
+ int r,_status;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as CONTROLLED",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string);
+
+ nr_stun_client_reset(pair->stun_client);
+ pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLED;
+
+ if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_BINDING_REQUEST,nr_ice_candidate_pair_stun_cb,pair))
+@@ -556,21 +571,21 @@
+
+
+
+ static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair,
+ nr_ice_candidate *lcand, nr_ice_candidate *rcand)
+ {
+ int r,_status;
+ char *as_string=0;
+
+ if(r=nr_concat_strings(&as_string,lcand->addr.as_string,"|",
+- rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",0))
++ rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",NULL))
+ ABORT(r);
+
+ nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword);
+
+ _status=0;
+ abort:
+ RFREE(as_string);
+ return;
+ }
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.h src/ice/ice_candidate_pair.h
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.h 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_candidate_pair.h 2012-10-06 08:30:22.000000000 -0700
+@@ -72,18 +72,18 @@
+
+ int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp);
+ int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
+ int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
+ int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state);
+ int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out);
+ int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair);
+ int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair);
+ int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair);
+ int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair);
+-void nr_ice_candidate_pair_restart_stun_nominated_cb(int s, int how, void *cb_arg);
++void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg);
+ int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp);
+
+ #ifdef __cplusplus
+ }
+ #endif /* __cplusplus */
+ #endif
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_component.c src/ice/ice_component.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_component.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_component.c 2012-10-06 08:30:22.000000000 -0700
+@@ -451,21 +451,21 @@
+ if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) {
+ *error=(r==R_NO_MEMORY)?500:400;
+ ABORT(r);
+ }
+ if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){
+ r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label);
+ *error=487;
+ ABORT(R_BAD_DATA);
+ }
+ pcand->priority=attr->u.priority;
+- pcand->state=NR_ICE_CAND_PEER_CANDIDATE;
++ pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED;;
+ TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp);
+
+ if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand,
+ &pair)) {
+ *error=(r==R_NO_MEMORY)?500:400;
+ ABORT(r);
+ }
+ nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN);
+
+ if(r=nr_ice_candidate_pair_insert(&comp->stream->check_list,pair)) {
+@@ -563,30 +563,38 @@
+ break;
+ }
+
+ /* PAIR with each peer*/
+ if(TAILQ_EMPTY(&pcomp->candidates)) {
+ /* can happen if our peer proposes no (or all bogus) candidates */
+ goto next_cand;
+ }
+ pcand=TAILQ_FIRST(&pcomp->candidates);
+ while(pcand){
+- nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword);
+- r_log(LOG_ICE,LOG_DEBUG,"Examining peer candidate %s:%s",codeword,pcand->label);
+-
+- if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair))
+- ABORT(r);
+-
+- if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list,
+- pair))
+- ABORT(r);
++ /* Only pair peer candidates which have not yet been paired.
++ This allows "trickle ICE". (Not yet standardized, but
++ part of WebRTC).
++
++ TODO(ekr@rtfm.com): Add refernece to the spec when there
++ is one.
++ */
++ if (pcand->state = NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED) {
++ nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword);
++ r_log(LOG_ICE,LOG_DEBUG,"Examining peer candidate %s:%s",codeword,pcand->label);
++
++ if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair))
++ ABORT(r);
+
++ if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list,
++ pair))
++ ABORT(r);
++ }
+ pcand=TAILQ_NEXT(pcand,entry_comp);
+ }
+
+ if(!pair)
+ ABORT(R_INTERNAL);
+
+ /* Add the stun username/password pair from the last pair (any
+ would do) to the stun contexts */
+ isock=STAILQ_FIRST(&lcomp->sockets);
+ while(isock){
+@@ -594,20 +602,28 @@
+ pair->r2l_user,&pair->r2l_pwd,nr_ice_component_stun_server_cb,pcomp))
+ ABORT(r);
+
+ isock=STAILQ_NEXT(isock,entry);
+ }
+
+ next_cand:
+ lcand=TAILQ_NEXT(lcand,entry_comp);
+ }
+
++ /* Mark all peer candidates as paired */
++ pcand=TAILQ_FIRST(&pcomp->candidates);
++ while(pcand){
++ pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED;
++
++ pcand=TAILQ_NEXT(pcand,entry_comp);
++ }
++
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+ int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
+ {
+ int r,_status;
+ int fire_cb=0;
+ nr_ice_cand_pair *p2;
+@@ -616,32 +632,32 @@
+ fire_cb=1;
+
+ /* Are we changing what the nominated pair is? */
+ if(comp->nominated){
+ if(comp->nominated->priority > pair->priority)
+ return(0);
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): replacing pair %s with pair %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->as_string,pair->as_string);
+ }
+
+ /* Set the new nominated pair */
+- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): nominated pair is %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,(int)pair);
++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): nominated pair is %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair);
+ comp->state=NR_ICE_COMPONENT_NOMINATED;
+ comp->nominated=pair;
+ comp->active=pair;
+
+- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling all pairs but %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,(int)pair);
++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling all pairs but %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair);
+
+ /* OK, we need to cancel off everything on this component */
+ p2=TAILQ_FIRST(&comp->stream->check_list);
+ while(p2){
+ if((p2 != pair) && (p2->remote->component->component_id == comp->component_id)){
+- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling pair %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->as_string,(int)p2);
++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling pair %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->as_string,p2);
+
+ if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2))
+ ABORT(r);
+ }
+
+ p2=TAILQ_NEXT(p2,entry);
+ }
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id);
+
+ if(r=nr_ice_media_stream_component_nominated(comp->stream,comp))
+@@ -734,21 +750,21 @@
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ RFREE(pairs);
+ return(_status);
+ }
+
+
+-static void nr_ice_component_keepalive_cb(int s, int how, void *cb_arg)
++static void nr_ice_component_keepalive_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_component *comp=cb_arg;
+ UINT4 keepalive_timeout;
+
+ assert(comp->keepalive_ctx);
+
+ if(NR_reg_get_uint4(NR_ICE_REG_KEEPALIVE_TIMER,&keepalive_timeout)){
+ keepalive_timeout=15000; /* Default */
+ }
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.c src/ice/ice_ctx.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_ctx.c 2012-10-06 08:30:22.000000000 -0700
+@@ -56,21 +56,21 @@
+ #include "util.h"
+
+
+ int LOG_ICE = 0;
+
+ static int nr_ice_random_string(char *str, int len);
+ static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out);
+ #ifdef USE_TURN
+ static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out);
+ #endif /* USE_TURN */
+-static void nr_ice_ctx_destroy_cb(int s, int how, void *cb_arg);
++static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg);
+
+ int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out)
+ {
+ int r,_status;
+ nr_ice_stun_server *servers = 0;
+ int i;
+ NR_registry child;
+ char *addr=0;
+ UINT2 port;
+ in_addr_t addr_int;
+@@ -271,21 +271,21 @@
+ *ctxp=ctx;
+
+ _status=0;
+ abort:
+ if(_status)
+ nr_ice_ctx_destroy_cb(0,0,ctx);
+
+ return(_status);
+ }
+
+-static void nr_ice_ctx_destroy_cb(int s, int how, void *cb_arg)
++static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_ctx *ctx=cb_arg;
+ nr_ice_foundation *f1,*f2;
+ nr_ice_media_stream *s1,*s2;
+ int i;
+ nr_ice_stun_id *id1,*id2;
+
+ RFREE(ctx->label);
+
+ RFREE(ctx->stun_servers);
+@@ -323,21 +323,21 @@
+ if(!ctxp || !*ctxp)
+ return(0);
+
+ NR_ASYNC_SCHEDULE(nr_ice_ctx_destroy_cb,*ctxp);
+
+ *ctxp=0;
+
+ return(0);
+ }
+
+-void nr_ice_initialize_finished_cb(int s, int h, void *cb_arg)
++void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg)
+ {
+ nr_ice_ctx *ctx=cb_arg;
+
+ /* r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Candidate %s %s",ctx->label,
+ cand->label, cand->state==NR_ICE_CAND_STATE_INITIALIZED?"INITIALIZED":"FAILED");
+ */
+ ctx->uninitialized_candidates--;
+
+ if(ctx->uninitialized_candidates==0){
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label);
+@@ -368,21 +368,22 @@
+ stream=STAILQ_FIRST(&ctx->streams);
+ while(stream){
+ if(r=nr_ice_media_stream_initialize(ctx,stream))
+ ABORT(r);
+
+ stream=STAILQ_NEXT(stream,entry);
+ }
+
+ if(ctx->uninitialized_candidates)
+ ABORT(R_WOULDBLOCK);
+-
++
++
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+ int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp)
+ {
+ int r,_status;
+
+ if(r=nr_ice_media_stream_create(ctx,label,components,streamp))
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.h src/ice/ice_ctx.h
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.h 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_ctx.h 2012-10-06 08:30:22.000000000 -0700
+@@ -92,23 +92,23 @@
+ UCHAR id[12];
+
+ STAILQ_ENTRY(nr_ice_stun_id_) entry;
+ } nr_ice_stun_id;
+
+ typedef STAILQ_HEAD(nr_ice_stun_id_head_,nr_ice_stun_id_) nr_ice_stun_id_head;
+
+ struct nr_ice_ctx_ {
+ UINT4 flags;
+ int state;
+-#define NR_ICE_STATE_CREATED 1
+-#define NR_ICE_STATE_INITIALIZING 2
+-#define NR_ICE_STATE_INITIALIZED 3
++#define NR_ICE_STATE_CREATED 1
++#define NR_ICE_STATE_INITIALIZING 2
++#define NR_ICE_STATE_INITIALIZED 3
+ char *label;
+
+ char *ufrag;
+ char *pwd;
+
+ UINT4 Ta;
+
+ nr_ice_stun_server *stun_servers; /* The list of stun servers */
+ int stun_server_ct;
+ nr_ice_turn_server *turn_servers; /* The list of turn servers */
+@@ -133,21 +133,21 @@
+
+ int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp);
+ #define NR_ICE_CTX_FLAGS_OFFERER 1
+ #define NR_ICE_CTX_FLAGS_ANSWERER (1<<1)
+ #define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1<<2)
+ #define NR_ICE_CTX_FLAGS_LITE (1<<3)
+
+ int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
+ int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
+ int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
+-void nr_ice_initialize_finished_cb(int s, int h, void *cb_arg);
++void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg);
+ int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
+ int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp);
+ int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
+ int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]);
+ int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg);
+ int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx);
+
+ extern int LOG_ICE;
+
+ #ifdef __cplusplus
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.c src/ice/ice_media_stream.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_media_stream.c 2012-10-06 08:30:22.000000000 -0700
+@@ -35,21 +35,21 @@
+ static char *RCSSTRING __UNUSED__="$Id: ice_media_stream.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+ #include <string.h>
+ #include <assert.h>
+ #include <nr_api.h>
+ #include <r_assoc.h>
+ #include <async_timer.h>
+ #include "ice_ctx.h"
+
+ static char *nr_ice_media_stream_states[]={"INVALID",
+- "FROZEN","ACTIVE","COMPLETED","FAILED"
++ "UNPAIRED","FROZEN","ACTIVE","COMPLETED","FAILED"
+ };
+
+ int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
+
+ int nr_ice_media_stream_create(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp)
+ {
+ int r,_status;
+ nr_ice_media_stream *stream=0;
+ nr_ice_component *comp=0;
+ int i;
+@@ -66,29 +66,29 @@
+ for(i=0;i<components;i++){
+ /* component-id must be > 0, so increment by 1 */
+ if(r=nr_ice_component_create(stream, i+1, &comp))
+ ABORT(r);
+
+ }
+
+ TAILQ_INIT(&stream->check_list);
+
+ stream->component_ct=components;
+-
++ stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED;
+ *streamp=stream;
+
+ _status=0;
+ abort:
+ if(_status){
+ nr_ice_media_stream_destroy(&stream);
+ }
+- return(_status);
++ return(_status);
+ }
+
+ int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp)
+ {
+ nr_ice_media_stream *stream;
+ nr_ice_component *c1,*c2;
+ nr_ice_cand_pair *p1,*p2;
+ if(!streamp || !*streamp)
+ return(0);
+
+@@ -200,85 +200,148 @@
+ if(attrs){
+ for(index=0;index<attrct;index++){
+ RFREE(attrs[index]);
+ }
+ RFREE(attrs);
+ }
+ }
+ return(_status);
+ }
+
++
++/* Get a default candidate per 4.1.4 */
++int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp)
++ {
++ int _status;
++ nr_ice_component *comp;
++ nr_ice_candidate *cand;
++ nr_ice_candidate *best_cand = NULL;
++
++ comp=STAILQ_FIRST(&stream->components);
++ while(comp){
++ if (comp->component_id == component)
++ break;
++
++ comp=STAILQ_NEXT(comp,entry);
++ }
++
++ if (!comp)
++ ABORT(R_NOT_FOUND);
++
++ /* We have the component. Now find the "best" candidate, making
++ use of the fact that more "reliable" candidate types have
++ higher numbers. So, we sort by type and then priority within
++ type
++ */
++ cand=TAILQ_FIRST(&comp->candidates);
++ while(cand){
++ if (!best_cand) {
++ best_cand = cand;
++ }
++ else {
++ if (best_cand->type < cand->type) {
++ best_cand = cand;
++ } else if (best_cand->type == cand->type) {
++ if (best_cand->priority < cand->priority)
++ best_cand = cand;
++ }
++ }
++
++ cand=TAILQ_NEXT(cand,entry_comp);
++ }
++
++ /* No candidates */
++ if (!best_cand)
++ ABORT(R_NOT_FOUND);
++
++ *candp = best_cand;
++
++ _status=0;
++ abort:
++ return(_status);
++ }
++
++
+ int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream)
+ {
+ int r,_status;
+ nr_ice_component *pcomp,*lcomp;
+
+ pcomp=STAILQ_FIRST(&pstream->components);
+ lcomp=STAILQ_FIRST(&lstream->components);
+ while(pcomp){
+ if(r=nr_ice_component_pair_candidates(pctx,lcomp,pcomp))
+ ABORT(r);
+-
++
+ lcomp=STAILQ_NEXT(lcomp,entry);
+ pcomp=STAILQ_NEXT(pcomp,entry);
+ };
+
++ if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) {
++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): unfreezing stream %s",pstream->pctx->label,pstream->label);
++ pstream->ice_state = NR_ICE_MEDIA_STREAM_CHECKS_FROZEN;
++ }
++
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+ /* S 5.8 -- run the highest priority WAITING pair or if not available
+ FROZEN pair */
+-static void nr_ice_media_stream_check_timer_cb(int s, int h, void *cb_arg)
++static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg)
+ {
+ int r,_status;
+ nr_ice_media_stream *stream=cb_arg;
+ nr_ice_cand_pair *pair;
+ int timer_val;
+
+ assert(stream->pctx->active_streams!=0);
+
+ timer_val=stream->pctx->ctx->Ta*stream->pctx->active_streams;
+
++ if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): bogus state for stream %s",stream->pctx->label,stream->label);
++ }
+ assert(stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED);
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label);
+ stream->timer=0;
+
+-
+ /* Find the highest priority WAITING check and move it to RUNNING */
+ pair=TAILQ_FIRST(&stream->check_list);
+ while(pair){
+ if(pair->state==NR_ICE_PAIR_STATE_WAITING)
+ break;
+ pair=TAILQ_NEXT(pair,entry);
+ }
+
+ /* Hmmm... No WAITING. Let's look for FROZEN */
+ if(!pair){
+ pair=TAILQ_FIRST(&stream->check_list);
+-
++
+ while(pair){
+ if(pair->state==NR_ICE_PAIR_STATE_FROZEN){
+ if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair))
+ ABORT(r);
+ break;
+ }
+ pair=TAILQ_NEXT(pair,entry);
+ }
+ }
+
+ if(pair){
+ nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */
+ NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer);
+ }
++ /* TODO(ekr@rtfm.com): Report on the special case where there are no checks to
++ run at all */
+ _status=0;
+ abort:
+ return;
+ }
+
+
+ /* Start checks for this media stream (aka check list) */
+ int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
+ {
+ assert(stream->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FROZEN);
+@@ -476,21 +539,23 @@
+ /* All done... */
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all components have nominated candidate pairs",stream->pctx->label,stream->label);
+ nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED);
+
+ /* Cancel our timer */
+ if(stream->timer){
+ NR_async_timer_cancel(stream->timer);
+ stream->timer=0;
+ }
+
+- stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream);
++ if (stream->pctx->handler) {
++ stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream);
++ }
+
+ /* Now tell the peer_ctx that we're done */
+ if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream))
+ ABORT(r);
+
+ done:
+ _status=0;
+ abort:
+ return(_status);
+ }
+@@ -515,21 +580,23 @@
+
+ p2=TAILQ_NEXT(p2,entry);
+ }
+
+ /* Cancel our timer */
+ if(stream->timer){
+ NR_async_timer_cancel(stream->timer);
+ stream->timer=0;
+ }
+
+- stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream);
++ if (stream->pctx->handler) {
++ stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream);
++ }
+
+ /* Now tell the peer_ctx that we're done */
+ if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.h src/ice/ice_media_stream.h
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.h 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_media_stream.h 2012-10-06 08:30:22.000000000 -0700
+@@ -45,40 +45,43 @@
+ struct nr_ice_peer_ctx_ *pctx;
+
+ struct nr_ice_media_stream_ *local_stream; /* used when this is a peer */
+ int component_ct;
+ nr_ice_component_head components;
+
+ char *ufrag; /* ICE username */
+ char *pwd; /* ICE password */
+
+ int ice_state;
+-#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 1
+-#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 2
+-#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 3
+-#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 4
++
++#define NR_ICE_MEDIA_STREAM_UNPAIRED 1
++#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2
++#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 3
++#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 4
++#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5
+
+ nr_ice_cand_pair_head check_list;
+ void *timer; /* Check list periodic timer */
+
+ // nr_ice_cand_pair_head valid_list;
+-
++
+ STAILQ_ENTRY(nr_ice_media_stream_) entry;
+ };
+
+ typedef STAILQ_HEAD(nr_ice_media_stream_head_,nr_ice_media_stream_) nr_ice_media_stream_head;
+
+ int nr_ice_media_stream_create(struct nr_ice_ctx_ *ctx,char *label, int components, nr_ice_media_stream **streamp);
+ int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp);
+ int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr);
+ int nr_ice_media_stream_initialize(struct nr_ice_ctx_ *ctx, nr_ice_media_stream *stream);
+ int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp,int *attrctp);
++int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp);
+ int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream);
+ int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+ int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+ int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation);
+ int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out);
+ int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component);
+ int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component);
+ int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
+ int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp);
+ int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx,nr_ice_media_stream *str, int component, UCHAR *data, int len);
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_parser.c src/ice/ice_parser.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_parser.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_parser.c 2012-10-06 08:30:22.000000000 -0700
+@@ -35,20 +35,21 @@
+ static char *RCSSTRING __UNUSED__="$Id: ice_parser.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+ #include <csi_platform.h>
+ #include <sys/types.h>
+ #ifdef WIN32
+ #include <winsock2.h>
+ #else
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
++#include <strings.h>
+ #endif
+ #include <string.h>
+ #include <assert.h>
+ #include <ctype.h>
+ #include "nr_api.h"
+ #include "ice_ctx.h"
+ #include "ice_candidate.h"
+ #include "ice_reg.h"
+
+ static void
+@@ -125,21 +126,21 @@
+ char *rel_addr=0;
+
+ if(!(cand=RCALLOC(sizeof(nr_ice_candidate))))
+ ABORT(R_NO_MEMORY);
+
+ if(!(cand->label=r_strdup(orig)))
+ ABORT(R_NO_MEMORY);
+
+ cand->ctx=ctx;
+ cand->isock=0;
+- cand->state=NR_ICE_CAND_PEER_CANDIDATE;
++ cand->state=NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED;
+ cand->stream=stream;
+ skip_whitespace(&str);
+
+ /* Candidate attr */
+ if (strncasecmp(str, "candidate:", 10))
+ ABORT(R_BAD_DATA);
+
+ fast_forward(&str, 10);
+ if (*str == '\0')
+ ABORT(R_BAD_DATA);
+@@ -311,26 +312,31 @@
+ /* it's expected to be at EOD at this point */
+
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ skip_whitespace(&str);
+
+- assert(strlen(str) == 0);
+-
++ /* This used to be an assert, but we don't want to exit on invalid
++ remote data */
++ if (strlen(str) != 0) {
++ ABORT(R_BAD_DATA);
++ }
++
+ *candp=cand;
+
+ _status=0;
+ abort:
++ /* TODO(ekr@rtfm.com): Fix memory leak if we have a parse error */
+ if (_status)
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error parsing attribute: %s",ctx->label,orig);
+
+ RFREE(connection_address);
+ RFREE(rel_addr);
+ return(_status);
+ }
+
+
+ int
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.c src/ice/ice_peer_ctx.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_peer_ctx.c 2012-10-06 08:30:22.000000000 -0700
+@@ -35,33 +35,35 @@
+ static char *RCSSTRING __UNUSED__="$Id: ice_peer_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $";
+
+ #include <string.h>
+ #include <assert.h>
+ #include <nr_api.h>
+ #include "ice_ctx.h"
+ #include "ice_peer_ctx.h"
+ #include "nr_crypto.h"
+ #include "async_timer.h"
+
+-static void nr_ice_peer_ctx_destroy_cb(int s, int how, void *cb_arg);
++static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg);
++static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct);
++static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate);
+
+ int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp)
+ {
+ int r,_status;
+ nr_ice_peer_ctx *pctx=0;
+
+ if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ if(!(pctx->label=r_strdup(label)))
+ ABORT(R_NO_MEMORY);
+-
++
+ pctx->ctx=ctx;
+ pctx->handler=handler;
+
+ /* Decide controlling vs. controlled */
+ if(ctx->flags & NR_ICE_CTX_FLAGS_LITE){
+ if(pctx->peer_lite){
+ r_log(LOG_ICE,LOG_ERR,"Both sides are ICE-Lite");
+ ABORT(R_BAD_DATA);
+ }
+
+@@ -88,85 +90,177 @@
+ nr_ice_peer_ctx_destroy_cb(0,0,pctx);
+ }
+ return(_status);
+ }
+
+
+
+ int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct)
+ {
+ nr_ice_media_stream *pstream=0;
+- nr_ice_candidate *cand=0;
+ nr_ice_component *comp,*comp2;
+ int r,_status;
+- int i,j;
+
+- /* Note: use component_ct from our own stream since components other
+- than this offered by the other side are unusable */
++ /*
++ Note: use component_ct from our own stream since components other
++ than this offered by the other side are unusable */
+ if(r=nr_ice_media_stream_create(pctx->ctx,stream->label,stream->component_ct,&pstream))
+ ABORT(r);
+-
+- /* Match up the local and remote components */
++
++ /* Match up the local and remote components */
+ comp=STAILQ_FIRST(&stream->components);
+ comp2=STAILQ_FIRST(&pstream->components);
+ while(comp){
+ comp2->local_component=comp;
+
+ comp=STAILQ_NEXT(comp,entry);
+ comp2=STAILQ_NEXT(comp2,entry);
+ }
+-
+
+- pstream->ice_state=NR_ICE_MEDIA_STREAM_CHECKS_FROZEN;
+ pstream->local_stream=stream;
+ pstream->pctx=pctx;
+
++ if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct))
++ ABORT(r);
++
++
++ STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry);
++
++ _status=0;
++ abort:
++ return(_status);
++ }
++
++static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct)
++ {
++ int r;
++ int i;
++
+ for(i=0;i<attr_ct;i++){
+ if(!strncmp(attrs[i],"ice-",4)){
+- if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i]))
++ if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) {
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus ICE attribute",pctx->ctx->label,pctx->label);
+ continue;
+- continue;
++ }
+ }
+-
+- if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,attrs[i],pstream,&cand))
+- continue;
+- if(cand->component_id-1>=pstream->component_ct){
+- r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label);
+- continue;
++ else if (!strncmp(attrs[i],"candidate",9)){
++ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,attrs[i])) {
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus candidate",pctx->ctx->label,pctx->label);
++ continue;
++ }
++ }
++ else {
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus attribute",pctx->ctx->label,pctx->label);
+ }
++ }
+
+- /* Not the fastest way to find a component, but it's what we got */
+- j=1;
+- for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){
+- if(j==cand->component_id)
+- break;
++ /* Doesn't fail because we just skip errors */
++ return(0);
++ }
+
+- j++;
+- }
+-
+- if(!comp){
+- r_log(LOG_ICE,LOG_ERR,"Peer answered with more components than we offered");
+- ABORT(R_BAD_DATA);
+- }
+-
+- cand->component=comp;
++static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate)
++ {
++ nr_ice_candidate *cand=0;
++ nr_ice_component *comp;
++ int j;
++ int r, _status;
+
+- TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp);
++ if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,candidate,pstream,&cand))
++ ABORT(r);
++ if(cand->component_id-1>=pstream->component_ct){
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label);
++ ABORT(R_BAD_DATA);
+ }
+
+- STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry);
++ /* Not the fastest way to find a component, but it's what we got */
++ j=1;
++ for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){
++ if(j==cand->component_id)
++ break;
++
++ j++;
++ }
++
++ if(!comp){
++ r_log(LOG_ICE,LOG_ERR,"Peer answered with more components than we offered");
++ ABORT(R_BAD_DATA);
++ }
++
++ cand->component=comp;
++
++ TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp);
+
+ _status=0;
+- abort:
++ abort:
++ if (_status) {
++ nr_ice_candidate_destroy(&cand);
++ }
+ return(_status);
+ }
+
++
++
++int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate)
++ {
++ /* First need to find the stream. Because we don't have forward pointers,
++ iterate through all the peer streams to find one that matches us */
++ nr_ice_media_stream *pstream;
++ int r,_status;
++ int needs_pairing = 0;
++
++ pstream=STAILQ_FIRST(&pctx->peer_streams);
++ while(pstream) {
++ if (pstream->local_stream == stream)
++ break;
++
++ pstream = STAILQ_NEXT(pstream, entry);
++ }
++ if (!pstream) {
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label);
++ ABORT(R_NOT_FOUND);
++ }
++
++ switch(pstream->ice_state) {
++ case NR_ICE_MEDIA_STREAM_UNPAIRED:
++ break;
++ case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN:
++ case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE:
++ needs_pairing = 1;
++ break;
++ default:
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) tried to trickle ICE in inappropriate state %d",pctx->ctx->label,pctx->label,stream->label,pstream->ice_state);
++ ABORT(R_ALREADY);
++ break;
++ }
++
++ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,candidate)){
++ ABORT(r);
++ }
++
++ /* If ICE is running (i.e., we are in FROZEN or ACTIVE states)
++ then we need to pair this new candidate. For now we
++ just re-pair the stream which is inefficient but still
++ fine because we suppress duplicate pairing */
++ if (needs_pairing) {
++ if(r=nr_ice_media_stream_pair_candidates(pctx, stream, pstream)) {
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to pair trickle ICE candidates",pctx->ctx->label,pctx->label,stream->label);
++ ABORT(r);
++ }
++ }
++
++ _status =0;
++ abort:
++ return(_status);
++
++ }
++
++
+ int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *stream;
+ int r,_status;
+
+ if(STAILQ_EMPTY(&pctx->peer_streams)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attribributes",pctx->ctx->label,pctx->label);
+ ABORT(R_FAILED);
+ }
+
+@@ -177,21 +271,21 @@
+ ABORT(r);
+
+ stream=STAILQ_NEXT(stream,entry);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+-static void nr_ice_peer_ctx_destroy_cb(int s, int how, void *cb_arg)
++static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_peer_ctx *pctx=cb_arg;
+ nr_ice_media_stream *str1,*str2;
+
+ RFREE(pctx->label);
+ RFREE(pctx->peer_ufrag);
+ RFREE(pctx->peer_pwd);
+
+ STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){
+ STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry);
+@@ -199,44 +293,79 @@
+ }
+
+ RFREE(pctx);
+ }
+
+ int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp)
+ {
+
+ if(!pctxp || !*pctxp)
+ return(0);
+-
++
++ /* Stop calling the handler */
++ (*pctxp)->handler = 0;
++
+ NR_ASYNC_SCHEDULE(nr_ice_peer_ctx_destroy_cb,*pctxp);
+
+ *pctxp=0;
+
+ return(0);
+ }
+
++
+ /* Start the checks for the first media stream (S 5.7)
+ The rest remain FROZEN */
+ int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx)
+ {
++ return nr_ice_peer_ctx_start_checks2(pctx, 0);
++ }
++
++/* Start checks for some media stream.
++
++ If allow_non_first == 0, then we only look at the first stream,
++ which is 5245-complaint.
++
++ If allow_non_first == 1 then we find the first non-empty stream
++ This is not compliant with RFC 5245 but is necessary to make trickle ICE
++ work plausibly
++*/
++int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first)
++ {
+ int r,_status;
+ nr_ice_media_stream *stream;
+
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ if(!stream)
+ ABORT(R_FAILED);
+
++ while (stream) {
++ if(!TAILQ_EMPTY(&stream->check_list))
++ break;
++
++ if(!allow_non_first){
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label);
++ ABORT(R_FAILED);
++ }
++
++ stream=STAILQ_NEXT(stream, entry);
++ }
++
++ if (!stream) {
++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label);
++ ABORT(R_NOT_FOUND);
++ }
++
+ if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) {
+- if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream))
+- ABORT(r);
+- if(r=nr_ice_media_stream_start_checks(pctx,stream))
+- ABORT(r);
++ if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream))
++ ABORT(r);
++ if(r=nr_ice_media_stream_start_checks(pctx,stream))
++ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+ #ifndef NDEBUG
+ int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out)
+ {
+@@ -253,26 +382,28 @@
+ stream=STAILQ_NEXT(stream,entry);
+ }
+ fprintf(out,"==========================================\n");
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+ #endif
+
+-static void nr_ice_peer_ctx_fire_done(int s, int how, void *cb_arg)
++static void nr_ice_peer_ctx_fire_done(NR_SOCKET s, int how, void *cb_arg)
+ {
+ nr_ice_peer_ctx *pctx=cb_arg;
+
+ /* Fire the handler callback to say we're done */
+- pctx->handler->vtbl->ice_completed(pctx->handler->obj, pctx);
++ if (pctx->handler) {
++ pctx->handler->vtbl->ice_completed(pctx->handler->obj, pctx);
++ }
+ }
+
+
+ /* OK, a stream just went ready. Examine all the streams to see if we're
+ maybe miraculously done */
+ int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream)
+ {
+ int _status;
+ nr_ice_media_stream *str;
+ int failed=0;
+@@ -365,21 +496,24 @@
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL))
+ break;
+
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+
+ if(!cand)
+ ABORT(R_REJECTED);
+
+ /* OK, there's a match. Call the handler */
+- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label);
+
+- pctx->handler->vtbl->msg_recvd(pctx->handler->obj,
+- pctx,comp->stream,comp->component_id,data,len);
++ if (pctx->handler) {
++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label);
++
++ pctx->handler->vtbl->msg_recvd(pctx->handler->obj,
++ pctx,comp->stream,comp->component_id,data,len);
++ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.h src/ice/ice_peer_ctx.h
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.h 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_peer_ctx.h 2012-10-06 08:30:22.000000000 -0700
+@@ -33,23 +33,21 @@
+
+
+ #ifndef _ice_peer_ctx_h
+ #define _ice_peer_ctx_h
+ #ifdef __cplusplus
+ using namespace std;
+ extern "C" {
+ #endif /* __cplusplus */
+
+ struct nr_ice_peer_ctx_ {
+- int state;
+ char *label;
+-
+ nr_ice_ctx *ctx;
+ nr_ice_handler *handler;
+
+ UCHAR controlling; /* 1 for controlling, 0 for controlled */
+ UINT8 tiebreaker;
+
+ char *peer_ufrag;
+ char *peer_pwd;
+ int peer_lite;
+ int peer_ice_mismatch;
+@@ -59,23 +57,26 @@
+ int waiting_pairs;
+
+ STAILQ_ENTRY(nr_ice_peer_ctx_) entry;
+ };
+
+ typedef STAILQ_HEAD(nr_ice_peer_ctx_head_, nr_ice_peer_ctx_) nr_ice_peer_ctx_head;
+
+ int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp);
+ int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp);
+ int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct);
++int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand);
++
+ int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx);
+ int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct);
+ int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx);
++int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first);
+ int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out);
+ int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx);
+ int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+ int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp);
+ int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
+ #ifdef __cplusplus
+ }
+ #endif /* __cplusplus */
+ #endif
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_socket.c src/ice/ice_socket.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_socket.c 2012-09-16 16:26:08.000000000 -0700
++++ src/ice/ice_socket.c 2012-10-06 08:30:22.000000000 -0700
+@@ -216,28 +216,34 @@
+ RFREE(s1);
+ }
+
+ RFREE(isock);
+
+ return(0);
+ }
+
+ int nr_ice_socket_close(nr_ice_socket *isock)
+ {
++#ifdef NR_SOCKET_IS_VOID_PTR
++ NR_SOCKET fd=NULL;
++ NR_SOCKET no_socket = NULL;
++#else
+ NR_SOCKET fd=-1;
++ NR_SOCKET no_socket = -1;
++#endif
+
+ if (!isock||!isock->sock)
+ return(0);
+
+ nr_socket_getfd(isock->sock,&fd);
+ assert(isock->sock!=0);
+- if(fd!=-1){
++ if(fd != no_socket){
+ NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_READ);
+ NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_WRITE);
+ nr_socket_destroy(&isock->sock);
+ }
+
+ return(0);
+ }
+
+ int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle)
+ {
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/net/nr_socket.h src/net/nr_socket.h
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/net/nr_socket.h 2012-09-16 16:26:09.000000000 -0700
++++ src/net/nr_socket.h 2012-10-06 08:30:22.000000000 -0700
+@@ -38,21 +38,23 @@
+ #include <sys/types.h>
+ #ifdef WIN32
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #else
+ #include <sys/socket.h>
+ #endif
+
+ #include "transport_addr.h"
+
+-#ifdef WIN32
++#ifdef __cplusplus
++#define restrict
++#elif defined(WIN32)
+ #define restrict __restrict
+ #endif
+
+ typedef struct nr_socket_vtbl_ {
+ int (*destroy)(void **obj);
+ int (*ssendto)(void *obj,const void *msg, size_t len, int flags,
+ nr_transport_addr *addr);
+ int (*srecvfrom)(void *obj,void * restrict buf, size_t maxlen, size_t *len, int flags,
+ nr_transport_addr *addr);
+ int (*getfd)(void *obj, NR_SOCKET *fd);
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/net/transport_addr_reg.c src/net/transport_addr_reg.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/net/transport_addr_reg.c 2012-09-16 16:26:09.000000000 -0700
++++ src/net/transport_addr_reg.c 2012-10-06 08:30:22.000000000 -0700
+@@ -29,25 +29,27 @@
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+ static char *RCSSTRING __UNUSED__="$Id: transport_addr_reg.c,v 1.2 2008/04/28 17:59:03 ekr Exp $";
+
+ #include <csi_platform.h>
+ #include <stdio.h>
++#include <string.h>
+ #include <memory.h>
+ #include <sys/types.h>
+ #ifdef WIN32
+ #include <winsock2.h>
+ #else
++#include <strings.h>
+ #include <unistd.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #endif
+ #include <assert.h>
+ #include "nr_api.h"
+ #include "transport_addr.h"
+ #include "transport_addr_reg.h"
+
+@@ -83,20 +85,22 @@
+
+ if ((r=NR_reg_get2_uint2(prefix, "port", &port))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ port = 0;
+ }
+
+ if ((r=NR_reg_alloc2_string(prefix, "protocol", &protocol))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
++ p = IPPROTO_UDP;
++
+ protocol = 0;
+ }
+ else {
+ if (!strcasecmp("tcp", protocol))
+ p = IPPROTO_TCP;
+ else if (!strcasecmp("udp", protocol))
+ p = IPPROTO_UDP;
+ else
+ ABORT(R_BAD_DATA);
+ }
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/addrs.c src/stun/addrs.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/addrs.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/addrs.c 2012-10-06 09:42:43.000000000 -0700
+@@ -46,20 +46,22 @@
+ #include <sys/sysctl.h>
+ #include <sys/param.h>
+ #include <sys/socket.h>
+ #include <sys/syslog.h>
+ #include <net/if.h>
+ #ifndef LINUX
+ #include <net/if_var.h>
+ #include <net/if_dl.h>
+ #include <net/if_types.h>
+ #include <sys/sockio.h>
++#else
++#include <linux/if.h>
+ #endif
+ #include <net/route.h>
+
+ /* IP */
+ #include <netinet/in.h>
+ #ifdef LINUX
+ #include "sys/ioctl.h"
+ #else
+ #include <netinet/in_var.h>
+ #endif
+@@ -105,20 +107,23 @@
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ #include <err.h>
+
+ static void stun_rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *);
+ static int stun_grab_addrs(char *name, int addrcount,
+ struct ifa_msghdr *ifam,
+ nr_transport_addr addrs[], int maxaddrs, int *count);
++static int
++nr_stun_is_duplicate_addr(nr_transport_addr addrs[], int count, nr_transport_addr *addr);
++
+
+ /*
+ * Expand the compacted form of addresses as returned via the
+ * configuration read via sysctl().
+ */
+ #define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+ #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+ static void
+@@ -135,21 +140,21 @@
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+ }
+
+ static int
+ stun_grab_addrs(char *name, int addrcount, struct ifa_msghdr *ifam, nr_transport_addr addrs[], int maxaddrs, int *count)
+ {
+ int r,_status;
+- NR_SOCKET s = -1;
++ int s = -1;
+ struct ifreq ifr;
+ struct rt_addrinfo info;
+ struct sockaddr_in *sin;
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+
+ if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "unable to obtain addresses from socket");
+ ABORT(R_FAILED);
+@@ -180,21 +185,20 @@
+ addrcount--;
+
+ if (*count >= maxaddrs) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Address list truncated at %d out of entries", maxaddrs, maxaddrs+addrcount);
+ break;
+ }
+
+ ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
+ }
+
+-
+ _status = 0;
+ abort:
+ if (s != -1) close(s);
+ return _status;
+ }
+
+ static int
+ stun_get_mib_addrs(nr_transport_addr addrs[], int maxaddrs, int *count)
+ {
+ int _status;
+@@ -551,44 +555,48 @@
+ #else
+
+ static int
+ stun_get_siocgifconf_addrs(nr_transport_addr addrs[], int maxaddrs, int *count)
+ {
+ struct ifconf ifc;
+ int _status;
+ int s = socket( AF_INET, SOCK_DGRAM, 0 );
+ int len = 100 * sizeof(struct ifreq);
+ int r;
++ int e;
++ char *ptr;
++ int tl;
++ int n;
++ struct ifreq ifr2;
+
+ char buf[ len ];
+
+ ifc.ifc_len = len;
+ ifc.ifc_buf = buf;
+
+- int e = ioctl(s,SIOCGIFCONF,&ifc);
+- char *ptr = buf;
+- int tl = ifc.ifc_len;
+- int n=0;
++ e = ioctl(s,SIOCGIFCONF,&ifc);
++ ptr = buf;
++ tl = ifc.ifc_len;
++ n=0;
+
+ while ( (tl > 0) && ( n < maxaddrs) )
+ {
+ struct ifreq* ifr = (struct ifreq *)ptr;
+
+ #ifdef LINUX
+- int si = sizeof(ifr->ifr_name) + sizeof(ifr->ifr_addr);
++ int si = sizeof(struct ifreq);
+ #else
+ int si = sizeof(ifr->ifr_name) + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr));
+ #endif
+ tl -= si;
+ ptr += si;
+
+- struct ifreq ifr2;
+ ifr2 = *ifr;
+
+ e = ioctl(s,SIOCGIFADDR,&ifr2);
+ if ( e == -1 )
+ {
+ continue;
+ }
+
+ //r_log(NR_LOG_STUN, LOG_ERR, "ioctl addr e = %d",e);
+
+@@ -603,21 +611,21 @@
+
+ close(s);
+
+ *count = n;
+
+ _status = 0;
+ return _status;
+ }
+ #endif
+
+-int
++static int
+ nr_stun_is_duplicate_addr(nr_transport_addr addrs[], int count, nr_transport_addr *addr)
+ {
+ int i;
+ int different;
+
+ for (i = 0; i < count; ++i) {
+ different = nr_transport_addr_cmp(&addrs[i], addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL);
+ if (!different)
+ return 1; /* duplicate */
+ }
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/nr_socket_turn.c src/stun/nr_socket_turn.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/nr_socket_turn.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/nr_socket_turn.c 2012-10-06 08:30:22.000000000 -0700
+@@ -246,17 +246,19 @@
+ default:
+ assert(0);
+ break;
+ }
+
+ return R_FAILED;
+ }
+
+ static int nr_socket_turn_close(void *obj)
+ {
++#ifndef NDEBUG
+ nr_socket_turn *sturn=obj;
+ assert(sturn->magic_cookie == nr_socket_turn_magic_cookie);
++#endif
+
+ return 0;
+ }
+
+ #endif /* USE_TURN */
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_client_ctx.c src/stun/stun_client_ctx.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_client_ctx.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/stun_client_ctx.c 2012-10-06 08:30:22.000000000 -0700
+@@ -38,21 +38,22 @@
+ #include <string.h>
+
+ #include <nr_api.h>
+ #include "stun.h"
+ #include "async_timer.h"
+ #include "registry.h"
+ #include "stun_reg.h"
+ #include "r_time.h"
+
+ static int nr_stun_client_send_request(nr_stun_client_ctx *ctx);
+-static void nr_stun_client_timer_expired_cb(int a, int b, void *cb_arg);
++static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg);
++static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password);
+
+ int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp)
+ {
+ nr_stun_client_ctx *ctx=0;
+ int r,_status;
+
+ if ((r=nr_stun_startup()))
+ ABORT(r);
+
+ if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx))))
+@@ -185,21 +186,21 @@
+ ctx->finished_cb = 0;
+ ctx->cb_arg = 0;
+ ctx->request_ct = 0;
+ ctx->timeout_ms = 0;
+
+ ctx->state = NR_STUN_CLIENT_STATE_INITTED;
+
+ return 0;
+ }
+
+-static void nr_stun_client_timer_expired_cb(int a, int b, void *cb_arg)
++static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg)
+ {
+ int _status;
+ nr_stun_client_ctx *ctx=cb_arg;
+ struct timeval now;
+ INT8 ms_waited;
+
+ /* Prevent this timer from being cancelled later */
+ ctx->timer_handle=0;
+
+ /* Shouldn't happen */
+@@ -387,21 +388,21 @@
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ ctx->state=NR_STUN_CLIENT_STATE_FAILED;
+ }
+ return(_status);
+ }
+
+-int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password)
++static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password)
+ {
+ *password = (Data*)arg;
+ if (!arg)
+ return(R_NOT_FOUND);
+ return(0);
+ }
+
+ int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr)
+ {
+ int r,_status;
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_codec.c src/stun/stun_codec.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_codec.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/stun_codec.c 2012-10-06 08:30:22.000000000 -0700
+@@ -73,20 +73,22 @@
+ static int nr_stun_decode_htonll(UCHAR *buf, int buflen, int *offset, UINT8 *data);
+ static int nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data);
+
+ static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, int len, void *data, int max_bytes, int max_chars);
+
+ static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+ static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+ static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+ static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
+ static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data);
++static int
++nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data);
+
+
+ int
+ nr_stun_encode_htons(UINT2 data, int buflen, UCHAR *buf, int *offset)
+ {
+ UINT2 d = htons(data);
+
+ if (*offset + sizeof(d) >= buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d >= %d", *offset, sizeof(d), buflen);
+ return R_BAD_DATA;
+@@ -632,21 +634,21 @@
+ }
+
+ fingerprint->checksum = checksum ^ 0x5354554e;
+
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum);
+
+ fingerprint->valid = 1;
+ return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen);
+ }
+
+-int
++static int
+ nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data)
+ {
+ int r,_status;
+ nr_stun_attr_fingerprint *fingerprint = data;
+ nr_stun_message_header *header = (nr_stun_message_header*)buf;
+ int length;
+ UINT4 checksum;
+
+ if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum)))
+ ABORT(r);
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_proc.c src/stun/stun_proc.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_proc.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/stun_proc.c 2012-10-06 08:30:22.000000000 -0700
+@@ -43,20 +43,22 @@
+ #include <time.h>
+ #else /* UNIX */
+ #include <string.h>
+ #endif /* end UNIX */
+ #include <assert.h>
+
+ #include "stun.h"
+ #include "stun_reg.h"
+ #include "registry.h"
+
++static int
++nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res);
+
+ /* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */
+ int
+ nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg)
+ {
+ int _status;
+ nr_stun_message_attribute *attr;
+
+ #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE
+ /* if this message was generated by an RFC 3489 impementation,
+@@ -371,21 +373,21 @@
+ /* nothing to check in this case */
+ break;
+ #endif /* USE_STUND_0_96 */
+ }
+
+ _status=0;
+ abort:
+ return _status;
+ }
+
+-int
++static int
+ nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res)
+ {
+ int r,_status;
+ char *realm = 0;
+ char *nonce;
+ UINT2 size;
+
+ if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm)))
+ ABORT(r);
+
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_server_ctx.c src/stun/stun_server_ctx.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_server_ctx.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/stun_server_ctx.c 2012-10-06 08:30:22.000000000 -0700
+@@ -114,21 +114,21 @@
+ STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry);
+
+ _status=0;
+ abort:
+ if(_status){
+ nr_stun_server_destroy_client(clnt);
+ }
+ return(_status);
+ }
+
+-int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password)
++static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password)
+ {
+ int _status;
+ nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg;
+ nr_stun_server_client *clnt = 0;
+ nr_stun_message_attribute *username_attribute;
+
+ if ((nr_stun_get_message_client(ctx, msg, &clnt))) {
+ if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) {
+ r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Missing Username",ctx->label);
+ ABORT(R_NOT_FOUND);
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_util.c src/stun/stun_util.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_util.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/stun_util.c 2012-10-06 08:30:22.000000000 -0700
+@@ -94,21 +94,20 @@
+ _status = 0;
+ abort:
+ return _status;
+ }
+
+ int
+ nr_stun_find_local_addresses(nr_transport_addr addrs[], int maxaddrs, int *count)
+ {
+ int r,_status;
+ NR_registry *children = 0;
+- int i;
+
+ if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count)))
+ if (r == R_NOT_FOUND)
+ *count = 0;
+ else
+ ABORT(r);
+
+ if (*count == 0) {
+ if ((r=nr_stun_get_addrs(addrs, maxaddrs, 1, count)))
+ ABORT(r);
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/turn_client_ctx.c src/stun/turn_client_ctx.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/turn_client_ctx.c 2012-09-16 16:26:10.000000000 -0700
++++ src/stun/turn_client_ctx.c 2012-10-06 08:30:22.000000000 -0700
+@@ -55,21 +55,24 @@
+ };
+
+ static int TURN_PHASE_MODE[NUMBER_OF_STUN_CTX] = {
+ NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST1,
+ NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST2,
+ NR_TURN_CLIENT_MODE_SET_ACTIVE_DEST_REQUEST
+ };
+
+
+ static int nr_turn_client_next_action(nr_turn_client_ctx *ctx, int stun_ctx_state);
+-static void nr_turn_client_cb(int s, int how, void *cb_arg);
++static void nr_turn_client_cb(NR_SOCKET s, int how, void *cb_arg);
++static int
++nr_turn_client_prepare_start(nr_turn_client_ctx *ctx, char *username, Data *password, UINT4 bandwidth_kbps, UINT4 lifetime_secs, NR_async_cb finished_cb, void *cb_arg);
++
+
+ int
+ nr_turn_client_next_action(nr_turn_client_ctx *ctx, int stun_ctx_state)
+ {
+ int r,_status;
+
+ assert(ctx->phase >= -1 && ctx->phase < NUMBER_OF_STUN_CTX);
+
+ switch (ctx->state) {
+ //case NR_TURN_CLIENT_STATE_ALLOCATING:
+@@ -147,21 +150,21 @@
+ * because as a side effect this ctx may be operated on in the
+ * callback */
+ finished_cb(0,0,ctx->cb_arg);
+ }
+ }
+
+ return(_status);
+ }
+
+ void
+-nr_turn_client_cb(int s, int how, void *cb_arg)
++nr_turn_client_cb(NR_SOCKET s, int how, void *cb_arg)
+ {
+ int r,_status;
+ nr_turn_client_ctx *ctx = (nr_turn_client_ctx*)cb_arg;
+ nr_stun_client_ctx *stun_ctx = ctx->stun_ctx[ctx->phase];
+
+ assert(ctx->phase >= 0);
+
+ if ((r=nr_turn_client_next_action(ctx, stun_ctx->state)))
+ ABORT(r);
+
+@@ -234,21 +237,21 @@
+
+ RFREE(ctx->username);
+ r_data_destroy(&ctx->password);
+
+ RFREE(ctx->label);
+ RFREE(ctx);
+
+ return(0);
+ }
+
+-int
++static int
+ nr_turn_client_prepare_start(nr_turn_client_ctx *ctx, char *username, Data *password, UINT4 bandwidth_kbps, UINT4 lifetime_secs, NR_async_cb finished_cb, void *cb_arg)
+ {
+ int r,_status;
+ nr_stun_client_allocate_request1_params *allocate_request1 = 0;
+ nr_stun_client_allocate_request2_params *allocate_request2 = 0;
+ nr_stun_client_allocate_response1_results *allocate_response1 = 0;
+ // nr_stun_client_allocate_response2_results *allocate_response2;
+
+ if (ctx->state != NR_TURN_CLIENT_STATE_INITTED)
+ ABORT(R_NOT_PERMITTED);
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/util/ice_util.c src/util/ice_util.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/util/ice_util.c 2012-09-16 16:26:10.000000000 -0700
++++ src/util/ice_util.c 2012-10-06 08:30:22.000000000 -0700
+@@ -31,20 +31,21 @@
+ */
+
+ #include <stdarg.h>
+
+
+ static char *RCSSTRING __UNUSED__="$Id: ice_util.c,v 1.2 2008/04/28 17:59:05 ekr Exp $";
+
+ #include <stdarg.h>
+ #include <string.h>
+ #include "nr_api.h"
++#include "ice_util.h"
+
+ int nr_concat_strings(char **outp,...)
+ {
+ va_list ap;
+ char *s,*out=0;
+ int len=0;
+ int _status;
+
+ va_start(ap,outp);
+ while(s=va_arg(ap,char *)){
+diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/util/mbslen.c src/util/mbslen.c
+--- /Users/ekr/dev/mtransport-import-references/nICEr/src/util/mbslen.c 2012-09-16 16:26:10.000000000 -0700
++++ src/util/mbslen.c 2012-10-06 08:31:01.000000000 -0700
+@@ -56,21 +56,21 @@
+ {
+ #ifdef DARWIN
+ static locale_t loc = 0;
+ static int initialized = 0;
+ #endif /* DARWIN */
+ #ifdef WIN32
+ char *my_locale=0;
+ unsigned int i;
+ #endif /* WIN32 */
+ int _status;
+- int nbytes;
++ size_t nbytes;
+ int nchars;
+ mbstate_t mbs;
+
+ #ifdef DARWIN
+ if (! initialized) {
+ initialized = 1;
+ loc = newlocale(LC_CTYPE_MASK, "UTF-8", LC_GLOBAL_LOCALE);
+ }
+
+ if (loc == 0) {
+@@ -102,25 +102,28 @@
+
+ memset(&mbs, 0, sizeof(mbs));
+ nchars = 0;
+
+ #ifdef DARWIN
+ while (*s != '\0' && (nbytes = mbrlen_l(s, strlen(s), &mbs, loc)) != 0)
+ #else
+ while (*s != '\0' && (nbytes = mbrlen(s, strlen(s), &mbs)) != 0)
+ #endif /* DARWIN */
+ {
+- assert(nbytes >= 0);
+- if (nbytes == (size_t)-1) /* should never happen */
++ if (nbytes == (size_t)-1) /* should never happen */ {
++ assert(0);
+ ABORT(R_INTERNAL);
+- if (nbytes == (size_t)-2) /* encoding error */
++ }
++ if (nbytes == (size_t)-2) /* encoding error */ {
++ assert(0);
+ ABORT(R_BAD_DATA);
++ }
+
+ s += nbytes;
+ ++nchars;
+ }
+
+ *ncharsp = nchars;
+
+ _status = 0;
+ abort:
+ #ifdef WIN32
diff --git a/media/mtransport/third_party/nrappkit/COPYRIGHT b/media/mtransport/third_party/nrappkit/COPYRIGHT
new file mode 100644
index 000000000..b0bb25595
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/COPYRIGHT
@@ -0,0 +1,159 @@
+
+Copyright (C) 2006, Network Resonance, Inc.
+All Rights Reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+This distribution also contains material from ssldump, tcpdump, and
+FreeBSD. The licenses are on the individual source files but follow
+here as well.
+
+SSLDUMP LICENSE
+Copyright (C) 1999-2001 RTFM, Inc.
+All Rights Reserved
+
+This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+<ekr@rtfm.com> and licensed by RTFM, Inc.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE ERIC RESCORLA AND RTFM ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+TCPDUMP LICENSE
+The manual page for this software is partially excerpted from
+the tcpdump manual page, which is subject to the following license:
+Copyright (c) 1987, 1988, 1989, 1990, 1991, 1992, 1994, 1995, 1996, 1997
+ The Regents of the University of California. All rights reserved.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that: (1) source code distributions
+retain the above copyright notice and this paragraph in its entirety, (2)
+distributions including binary code include the above copyright notice and
+this paragraph in its entirety in the documentation or other materials
+provided with the distribution, and (3) all advertising materials mentioning
+features or use of this software display the following acknowledgement:
+``This product includes software developed by the University of California,
+Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+the University nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior
+written permission.
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+The compilation of software known as FreeBSD is distributed under the
+following terms:
+
+Copyright (C) 1992-2004 The FreeBSD Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The 4.4BSD and 4.4BSD-Lite software is distributed under the following
+terms:
+
+All of the documentation and software included in the 4.4BSD and 4.4BSD-Lite
+Releases is copyrighted by The Regents of the University of California.
+
+Copyright 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
+ The Regents of the University of California. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+This product includes software developed by the University of
+California, Berkeley and its contributors.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
diff --git a/media/mtransport/third_party/nrappkit/IMPORT_FILES b/media/mtransport/third_party/nrappkit/IMPORT_FILES
new file mode 100644
index 000000000..0f80a52e3
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/IMPORT_FILES
@@ -0,0 +1,87 @@
+ # API
+ src/plugin/nr_plugin.h
+ src/share/nr_api.h
+ src/share/nr_common.h
+ src/share/nr_reg_keys.h
+
+ # libekr
+ src/util/libekr/assoc.h
+ src/util/libekr/debug.c
+ src/util/libekr/debug.h
+ src/util/libekr/r_assoc.c
+ src/util/libekr/r_assoc.h
+ src/util/libekr/r_common.h
+ src/util/libekr/r_crc32.c
+ src/util/libekr/r_crc32.h
+ src/util/libekr/r_data.c
+ src/util/libekr/r_data.h
+ src/util/libekr/r_defaults.h
+ src/util/libekr/r_errors.c
+ src/util/libekr/r_errors.h
+ src/util/libekr/r_includes.h
+ src/util/libekr/r_list.c
+ src/util/libekr/r_list.h
+ src/util/libekr/r_macros.h
+ src/util/libekr/r_memory.c
+ src/util/libekr/r_memory.h
+ src/util/libekr/r_replace.c
+ src/util/libekr/r_thread.h
+ src/util/libekr/r_time.c
+ src/util/libekr/r_time.h
+ src/util/libekr/r_types.h
+ src/util/libekr/debug.c
+ src/util/libekr/debug.h
+
+ # Utilities
+ src/util/byteorder.c
+ src/util/byteorder.h
+ src/util/hex.c
+ src/util/hex.h
+ src/util/p_buf.c
+ src/util/p_buf.h
+ src/util/util.c
+ src/util/util.h
+
+ # Events
+ src/event/async_timer.h
+ src/event/async_wait.h
+ src/event/async_wait_int.h
+
+ # Logging
+ src/log/r_log.c
+ src/log/r_log.h
+
+ # Registry
+ src/registry/c2ru.c
+ src/registry/c2ru.h
+ src/registry/registry.c
+ src/registry/registry.h
+ src/registry/registry_int.h
+ src/registry/registry_local.c
+ src/registry/registry_vtbl.h
+ src/registry/registrycb.c
+
+ src/port/darwin/include/csi_platform.h
+ src/port/win32/include/csi_platform.h
+ src/port/linux/include/csi_platform.h
+ src/port/linux/include/linux_funcs.h
+ src/port/linux/include/sys/ttycom.h
+ src/port/linux/port-impl.mk
+ # sys/queue.h manually copied into src/port/generic
+
+ # Stats
+ src/stats/nrstats.h
+
+ # Meta-files
+ COPYRIGHT
+ VERSION
+ README
+
+
+
+
+
+
+
+
+
diff --git a/media/mtransport/third_party/nrappkit/README b/media/mtransport/third_party/nrappkit/README
new file mode 100644
index 000000000..9cf43319b
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/README
@@ -0,0 +1,133 @@
+$Id: README,v 1.3 2007/11/21 00:09:10 adamcain Exp $
+
+nrappkit 1.0b2
+Copyright (C) 2006 Network Resonance, Inc.
+
+
+nrappkit is a toolkit for building standalone applications and
+appliances. It provides:
+
+- registry-based configuration (with change callbacks)
+- extensible command and configuration shell
+- extensible statistics system
+- configurable logging system
+- event and timer handling
+- generic plugin system
+- launcher daemon
+
+The contents of nrappkit were extracted from Network Resonance's
+product on the theory that they were generally useful for
+application developers.
+
+THIS PACKAGE DOES NOT GRANT A LICENSE OR RIGHT TO ANY OTHER NETWORK
+RESONANCE TECHNOLOGY OR SOFTWARE.
+
+
+
+BUILDING
+
+Builds are done semi-manually with port directories for each
+platform. There are pre-existing ports to FreeBSD, Linux (Ubuntu
+and Fedora Core), and Darwin (MacOSX). To build the system:
+
+ cd src/make/<platform>
+ gmake
+
+Some of the platforms come in several variants. Most notably,
+if a platform exists in "regular" and "-appliance" variant,
+this means that the regular variant just builds binaries intended
+to be run out of the make directory (for development) and the
+appliance variant is intended to be installed in a real system.
+
+By default we want to install things owned as user "pcecap".
+Either make this user or edit the Makefile to be a user you
+like (e.g., nobody).
+
+If you want to include the 'nrsh' command-line configuration
+tool in your build, you will need to make sure the line
+ BUILD_NRSH=yes
+appears (uncommented-out) in your platform Makefile. You will
+also need to to build OpenSSL and libedit and point your nrappkit
+Makefile to the correct paths. You can obtain these packages at:
+ openssl-0.9.7l
+ http://www.openssl.org/source/openssl-0.9.7l.tar.gz
+
+ libedit-20060829-2.9
+ http://freshmeat.net/redir/editline/53029/url_tgz/libedit-20060829-2.9.tar.gz
+
+
+INSTALLING
+If you're doing an appliance as opposed to a development build,
+you'll want to install it. This is easy:
+
+ su
+ gmake install
+
+Most binaries and libraries ends up in /usr/local/pcecap while
+data files are in /var/pcecap. However, you can tweak
+this in the Makefile. By default it's all owned by pcecap.
+
+To ensure that dynamic libraries are loaded correctly at runtime,
+you'd want to make sure the right directory is included in your
+LD_LIBRARY_PATH or via ldconfig.
+
+
+QUICK TOUR
+The build makes the following binaries that you may find useful:
+
+- captured -- the launcher (the name is historical)
+- registryd -- the registry daemon
+- nrregctl -- a registry control program
+- nrsh -- the command shell (when included in build)
+- nrstatsctl -- the stats control program
+
+Using the nrcapctl script is the easiest way to interact with
+the applications. It is run as "nrcapctl <command>" with the
+following commands recognized:
+
+ startup -- fires up captured, which in turn runs and
+ initializes the registry
+
+ shutdown -- kills captured and its child processes
+
+ status -- prints the running status of captured in
+ human-readable form
+
+ stat -- prints the running status of captured in
+ a form easily parsed by scripts
+
+ enable -- alters the mode.txt file so that captured
+ starts
+
+ disable -- alters the mode.txt file so that captured
+ does not start
+
+ clear-statistics -- equivalent to "nrstatsctl -z" (requires
+ that captured be running)
+
+Note: the "start" and "stop" nrcapctl commands do nothing as they
+use components not included in nrappkit. However the associated
+script logic in nrcapctl demonstrates how additional applications
+might be launched using nrcapctl and particular registry settings.
+
+
+EXTENDING
+When things come up, they're pretty dumb. You'll probably want to
+write your own applications, otherwise it's not clear why you're doing
+this. The general idea is that you write your application using the
+facilities that nrappkit provides and then write plugins to the
+nrappkit components as necessary. So, for example, say you want
+to write a network daemon. You would:
+
+ - configure the launcher to launch your daemon (using the registry,
+ naturally).
+ - make calls to the registry to get configuration data
+ - make calls to the logging system to log data
+ - implement a stats module to record statistics
+ - write a plugin to nrsh to let people configure your parameters
+
+Examples of some of this stuff can be found in examples/demo_plugin.
+Otherwise, read the source. More documentation will be on the way,
+hopefully.
+
+
diff --git a/media/mtransport/third_party/nrappkit/README_MOZILLA b/media/mtransport/third_party/nrappkit/README_MOZILLA
new file mode 100644
index 000000000..92102c83c
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/README_MOZILLA
@@ -0,0 +1,22 @@
+This import of nrappkit is a subset of the distribution at:
+
+ nrappkit.sourceforge.net
+
+
+The last revision included in this import was on Nov 25, 2008.
+This import includes a small nunber of portability fixes that
+have been submitted to the upstream repository and are included
+in upstream.diff.
+
+
+Out of the list in the README, we use:
+
+- registry-based configuration (with change callbacks)
+ [but without the registry daemon]
+- configurable logging system
+- event and timer handling
+ [though partly reimplemented]
+
+Also, we use a bunch of the generic utilities such as string handling,
+generic hash tables in C, yet another concrete type mapping, etc.
+
diff --git a/media/mtransport/third_party/nrappkit/VERSION b/media/mtransport/third_party/nrappkit/VERSION
new file mode 100644
index 000000000..3ef69b306
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/VERSION
@@ -0,0 +1 @@
+1.0b2
diff --git a/media/mtransport/third_party/nrappkit/nrappkit.gyp b/media/mtransport/third_party/nrappkit/nrappkit.gyp
new file mode 100644
index 000000000..c6ff29999
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/nrappkit.gyp
@@ -0,0 +1,234 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# nrappkit.gyp
+#
+#
+{
+ 'targets' : [
+ {
+ 'target_name' : 'nrappkit',
+ 'type' : 'static_library',
+
+ 'include_dirs' : [
+ # EXTERNAL
+ # INTERNAL
+ 'src/event',
+ 'src/log',
+ 'src/port/generic/include',
+ 'src/registry',
+ 'src/share',
+ 'src/stats',
+ 'src/util',
+ 'src/util/libekr',
+ ],
+
+ 'sources' : [
+ # Shared
+# './src/share/nr_api.h',
+ './src/share/nr_common.h',
+# './src/share/nr_dynlib.h',
+ './src/share/nr_reg_keys.h',
+# './src/share/nr_startup.c',
+# './src/share/nr_startup.h',
+# './src/share/nrappkit_static_plugins.c',
+ './src/port/generic/include'
+
+ # libekr
+ './src/util/libekr/assoc.h',
+ './src/util/libekr/debug.c',
+ './src/util/libekr/debug.h',
+ './src/util/libekr/r_assoc.c',
+ './src/util/libekr/r_assoc.h',
+# './src/util/libekr/r_assoc_test.c',
+ './src/util/libekr/r_common.h',
+ './src/util/libekr/r_crc32.c',
+ './src/util/libekr/r_crc32.h',
+ './src/util/libekr/r_data.c',
+ './src/util/libekr/r_data.h',
+ './src/util/libekr/r_defaults.h',
+ './src/util/libekr/r_errors.c',
+ './src/util/libekr/r_errors.h',
+ './src/util/libekr/r_includes.h',
+ './src/util/libekr/r_list.c',
+ './src/util/libekr/r_list.h',
+ './src/util/libekr/r_macros.h',
+ './src/util/libekr/r_memory.c',
+ './src/util/libekr/r_memory.h',
+ './src/util/libekr/r_replace.c',
+ './src/util/libekr/r_thread.h',
+ './src/util/libekr/r_time.c',
+ './src/util/libekr/r_time.h',
+ './src/util/libekr/r_types.h',
+ './src/util/libekr/debug.c',
+ './src/util/libekr/debug.h',
+
+ # Utilities
+ './src/util/byteorder.c',
+ './src/util/byteorder.h',
+ #'./src/util/escape.c',
+ #'./src/util/escape.h',
+ #'./src/util/filename.c',
+ #'./src/util/getopt.c',
+ #'./src/util/getopt.h',
+ './src/util/hex.c',
+ './src/util/hex.h',
+ #'./src/util/mem_util.c',
+ #'./src/util/mem_util.h',
+ #'./src/util/mutex.c',
+ #'./src/util/mutex.h',
+ './src/util/p_buf.c',
+ './src/util/p_buf.h',
+ #'./src/util/ssl_util.c',
+ #'./src/util/ssl_util.h',
+ './src/util/util.c',
+ './src/util/util.h',
+ #'./src/util/util_db.c',
+ #'./src/util/util_db.h',
+
+ # Events
+# './src/event/async_timer.c',
+ './src/event/async_timer.h',
+# './src/event/async_wait.c',
+ './src/event/async_wait.h',
+ './src/event/async_wait_int.h',
+
+ # Logging
+ './src/log/r_log.c',
+ './src/log/r_log.h',
+ #'./src/log/r_log_plugin.c',
+
+ # Registry
+ './src/registry/c2ru.c',
+ './src/registry/c2ru.h',
+ #'./src/registry/mod_registry/mod_registry.c',
+ #'./src/registry/nrregctl.c',
+ #'./src/registry/nrregistryctl.c',
+ './src/registry/registry.c',
+ './src/registry/registry.h',
+ './src/registry/registry_int.h',
+ './src/registry/registry_local.c',
+ #'./src/registry/registry_plugin.c',
+ './src/registry/registry_vtbl.h',
+ './src/registry/registrycb.c',
+ #'./src/registry/registryd.c',
+ #'./src/registry/regrpc.h',
+ #'./src/registry/regrpc_client.c',
+ #'./src/registry/regrpc_client.h',
+ #'./src/registry/regrpc_client_cb.c',
+ #'./src/registry/regrpc_clnt.c',
+ #'./src/registry/regrpc_server.c',
+ #'./src/registry/regrpc_svc.c',
+ #'./src/registry/regrpc_xdr.c',
+
+ # Statistics
+ #'./src/stats/nrstats.c',
+ #'./src/stats/nrstats.h',
+ #'./src/stats/nrstats_app.c',
+ #'./src/stats/nrstats_int.h',
+ #'./src/stats/nrstats_memory.c',
+ ],
+
+ 'defines' : [
+ 'SANITY_CHECKS',
+ 'R_PLATFORM_INT_TYPES=<stdint.h>',
+ 'R_DEFINED_INT2=int16_t',
+ 'R_DEFINED_UINT2=uint16_t',
+ 'R_DEFINED_INT4=int32_t',
+ 'R_DEFINED_UINT4=uint32_t',
+ 'R_DEFINED_INT8=int64_t',
+ 'R_DEFINED_UINT8=uint64_t',
+ ],
+
+ 'conditions' : [
+ ## Mac and BSDs
+ [ 'OS == "mac"', {
+ 'defines' : [
+ 'DARWIN',
+ ],
+ }],
+ [ 'os_bsd == 1', {
+ 'defines' : [
+ 'BSD',
+ ],
+ }],
+ [ 'OS == "mac" or OS == "ios" or os_bsd == 1', {
+ 'cflags_mozilla': [
+ '-Wall',
+ '-Wno-parentheses',
+ '-Wno-strict-prototypes',
+ '-Wmissing-prototypes',
+ ],
+ 'defines' : [
+ 'HAVE_LIBM=1',
+ 'HAVE_STRDUP=1',
+ 'HAVE_STRLCPY=1',
+ 'HAVE_SYS_TIME_H=1',
+ 'HAVE_VFPRINTF=1',
+ 'NEW_STDIO'
+ 'RETSIGTYPE=void',
+ 'TIME_WITH_SYS_TIME_H=1',
+ '__UNUSED__=__attribute__((unused))',
+ ],
+
+ 'include_dirs': [
+ 'src/port/darwin/include'
+ ],
+
+ 'sources': [
+ './src/port/darwin/include/csi_platform.h',
+ ],
+ }],
+
+ ## Win
+ [ 'OS == "win"', {
+ 'defines' : [
+ 'WIN',
+ '__UNUSED__=',
+ 'HAVE_STRDUP=1',
+ 'NO_REG_RPC'
+ ],
+
+ 'include_dirs': [
+ 'src/port/win32/include'
+ ],
+
+ 'sources': [
+ './src/port/win32/include/csi_platform.h',
+ ],
+ }],
+ ## Linux
+ [ '(OS == "linux") or (OS == "android")', {
+ 'cflags_mozilla': [
+ '-Wall',
+ '-Wno-parentheses',
+ '-Wno-strict-prototypes',
+ '-Wmissing-prototypes',
+ ],
+ 'defines' : [
+ 'LINUX',
+ 'HAVE_LIBM=1',
+ 'HAVE_STRDUP=1',
+ 'HAVE_STRLCPY=1',
+ 'HAVE_SYS_TIME_H=1',
+ 'HAVE_VFPRINTF=1',
+ 'NEW_STDIO'
+ 'RETSIGTYPE=void',
+ 'TIME_WITH_SYS_TIME_H=1',
+ 'NO_REG_RPC=1',
+ '__UNUSED__=__attribute__((unused))',
+ ],
+
+ 'include_dirs': [
+ 'src/port/linux/include'
+ ],
+ 'sources': [
+ './src/port/linux/include/csi_platform.h',
+ ],
+ }]
+ ]
+ }]
+}
+
+
diff --git a/media/mtransport/third_party/nrappkit/src/event/async_timer.h b/media/mtransport/third_party/nrappkit/src/event/async_timer.h
new file mode 100644
index 000000000..544a3d44f
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/event/async_timer.h
@@ -0,0 +1,54 @@
+/**
+ async_timer.h
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Sun Feb 22 19:35:24 2004
+ */
+
+
+#ifndef _async_timer_h
+#define _async_timer_h
+
+
+int NR_async_timer_init(void);
+int NR_async_timer_set(int delay_ms,NR_async_cb cb,void *cb_arg,char *function,int line,void **handle);
+int NR_async_timer_cancel(void *handle);
+int NR_async_timer_update_time(struct timeval *tv);
+int NR_async_timer_next_timeout(int *delay_ms);
+int NR_async_timer_sanity_check_for_cb_deleted(NR_async_cb cb,void *cb_arg);
+
+#define NR_ASYNC_TIMER_SET(d,c,a,hp) NR_async_timer_set(d,c,a,(char *)__FUNCTION__,__LINE__,hp)
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/event/async_wait.h b/media/mtransport/third_party/nrappkit/src/event/async_wait.h
new file mode 100644
index 000000000..58bcd435e
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/event/async_wait.h
@@ -0,0 +1,83 @@
+/**
+ async_wait.h
+
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Thu Dec 20 20:14:49 2001
+ */
+
+
+#ifndef _async_wait_h
+#define _async_wait_h
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <csi_platform.h>
+
+typedef void (*NR_async_cb)(NR_SOCKET resource,int how,void *arg);
+
+#define NR_ASYNC_WAIT_READ 0
+#define NR_ASYNC_WAIT_WRITE 1
+
+
+int NR_async_wait_init(void);
+int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg,
+ char *function,int line);
+int NR_async_cancel(NR_SOCKET sock,int how);
+int NR_async_schedule(NR_async_cb cb,void *arg,char *function,int line);
+int NR_async_event_wait(int *eventsp);
+int NR_async_event_wait2(int *eventsp,struct timeval *tv);
+
+
+#ifdef NR_DEBUG_ASYNC
+#define NR_ASYNC_WAIT(s,h,cb,arg) do { \
+fprintf(stderr,"NR_async_wait(%d,%s,%s) at %s(%d)\n",s,#h,#cb,__FUNCTION__,__LINE__); \
+ NR_async_wait(s,h,cb,arg,(char *)__FUNCTION__,__LINE__); \
+} while (0)
+#define NR_ASYNC_SCHEDULE(cb,arg) do { \
+fprintf(stderr,"NR_async_schedule(%s) at %s(%d)\n",#cb,__FUNCTION__,__LINE__);\
+ NR_async_schedule(cb,arg,(char *)__FUNCTION__,__LINE__); \
+} while (0)
+#define NR_ASYNC_CANCEL(s,h) do { \
+ fprintf(stderr,"NR_async_cancel(%d,%s) at %s(%d)\n",s,#h,(char *)__FUNCTION__,__LINE__); \
+NR_async_cancel(s,h); \
+} while (0)
+#else
+#define NR_ASYNC_WAIT(a,b,c,d) NR_async_wait(a,b,c,d,(char *)__FUNCTION__,__LINE__)
+#define NR_ASYNC_SCHEDULE(a,b) NR_async_schedule(a,b,(char *)__FUNCTION__,__LINE__)
+#define NR_ASYNC_CANCEL NR_async_cancel
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/event/async_wait_int.h b/media/mtransport/third_party/nrappkit/src/event/async_wait_int.h
new file mode 100644
index 000000000..d17372d18
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/event/async_wait_int.h
@@ -0,0 +1,62 @@
+/**
+ async_wait_int.h
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Sun Feb 22 19:16:01 2004
+ */
+
+
+#ifndef _async_wait_int_h
+#define _async_wait_int_h
+
+
+typedef struct callback_ {
+ NR_async_cb cb;
+ void *arg;
+ int how;
+ int resource;
+ void *backptr;
+
+ char *func;
+ int free_func;
+ int line;
+ TAILQ_ENTRY(callback_) entry;
+} callback;
+
+int nr_async_create_cb(NR_async_cb cb,void *arg,int how,
+ int resource,char *func,int line,callback **cbp);
+int nr_async_destroy_cb(callback **cbp);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/log/r_log.c b/media/mtransport/third_party/nrappkit/src/log/r_log.c
new file mode 100644
index 000000000..6580626e6
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/log/r_log.c
@@ -0,0 +1,694 @@
+/**
+ r_log.c
+
+
+ Copyright (C) 2001, RTFM, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Mon Dec 3 15:24:38 2001
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: r_log.c,v 1.10 2008/11/25 22:25:18 adamcain Exp $";
+
+
+#ifdef LINUX
+#define _BSD_SOURCE
+#endif
+
+#include "r_log.h"
+#include "hex.h"
+
+#include <string.h>
+#include <errno.h>
+#ifndef _MSC_VER
+#include <strings.h>
+#include <syslog.h>
+#endif
+#include <registry.h>
+#include <time.h>
+
+
+#include "nr_common.h"
+#include "nr_reg_keys.h"
+
+
+#define LOGGING_DEFAULT_LEVEL 5
+
+int NR_LOG_LOGGING = 0;
+
+static char *log_level_strings[]={
+ "EMERG",
+ "ALERT",
+ "CRIT",
+ "ERR",
+ "WARNING",
+ "NOTICE",
+ "INFO",
+ "DEBUG"
+};
+
+static char *log_level_reg_strings[]={
+ "emergency",
+ "alert",
+ "critical",
+ "error",
+ "warning",
+ "notice",
+ "info",
+ "debug"
+};
+
+#define LOGGING_REG_PREFIX "logging"
+
+#define MAX_ERROR_STRING_SIZE 512
+
+#define R_LOG_INITTED1 1
+#define R_LOG_INITTED2 2
+
+#define LOG_NUM_DESTINATIONS 3
+
+typedef struct log_type_ {
+ char *facility_name;
+ int level[LOG_NUM_DESTINATIONS];
+ NR_registry dest_facility_key[LOG_NUM_DESTINATIONS];
+} log_type;
+
+#define MAX_LOG_TYPES 16
+
+static log_type log_types[MAX_LOG_TYPES];
+static int log_type_ct;
+
+
+typedef struct log_destination_ {
+ char *dest_name;
+ int enabled;
+ int default_level;
+ r_dest_vlog *dest_vlog;
+} log_destination;
+
+
+#define LOG_LEVEL_UNDEFINED -1
+#define LOG_LEVEL_NONE -2
+#define LOG_LEVEL_USE_DEST_DEFAULT -3
+
+static int stderr_vlog(int facility,int level,const char *format,va_list ap);
+static int syslog_vlog(int facility,int level,const char *format,va_list ap);
+static int noop_vlog(int facility,int level,const char *format,va_list ap);
+
+static log_destination log_destinations[LOG_NUM_DESTINATIONS]={
+ {
+ "stderr",
+ 0,
+ LOGGING_DEFAULT_LEVEL,
+ stderr_vlog,
+ },
+ {
+ "syslog",
+#ifndef WIN32
+ 1,
+#else
+ 0,
+#endif
+ LOGGING_DEFAULT_LEVEL,
+ syslog_vlog,
+ },
+ {
+ "extra",
+ 0,
+ LOGGING_DEFAULT_LEVEL,
+ noop_vlog,
+ },
+};
+
+static int r_log_level=LOGGING_DEFAULT_LEVEL;
+static int r_log_level_environment=0;
+static int r_log_initted=0;
+static int r_log_env_verbose=0;
+
+static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name);
+static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name);
+static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name);
+static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name);
+static int r_log_get_default_level(void);
+static int r_log_get_destinations(int usereg);
+static int r_logging_dest(int dest_index, int facility, int level);
+static int _r_log_init(int usereg);
+static int r_log_get_reg_level(NR_registry name, int *level);
+
+int r_log_register(char *facility_name,int *log_facility)
+ {
+ int i,j;
+ int level;
+ int r,_status;
+ char *buf=0;
+ NR_registry dest_prefix, dest_facility_prefix;
+
+ for(i=0;i<log_type_ct;i++){
+ if(!strcmp(facility_name,log_types[i].facility_name)){
+ *log_facility=i;
+ return(0);
+ }
+ }
+
+ if(log_type_ct==MAX_LOG_TYPES){
+ ABORT(R_INTERNAL);
+ }
+
+ i=log_type_ct;
+
+ /* Initial registration completed, increment log_type_ct */
+ log_types[i].facility_name=r_strdup(facility_name);
+ *log_facility=log_type_ct;
+ log_type_ct++;
+
+ for(j=0; j<LOG_NUM_DESTINATIONS; j++){
+ log_types[i].level[j]=LOG_LEVEL_UNDEFINED;
+
+ if(NR_reg_initted()){
+
+ if(snprintf(dest_prefix,sizeof(NR_registry),
+ "logging.%s.facility",log_destinations[j].dest_name)>=sizeof(NR_registry))
+ ABORT(R_INTERNAL);
+
+ if (r=NR_reg_make_registry(dest_prefix,facility_name,dest_facility_prefix))
+ ABORT(r);
+
+ if(snprintf(log_types[i].dest_facility_key[j],sizeof(NR_registry),
+ "%s.level",dest_facility_prefix)>=sizeof(NR_registry))
+ ABORT(R_INTERNAL);
+
+ if(!r_log_get_reg_level(log_types[i].dest_facility_key[j],&level)){
+ log_types[i].level[j]=level;
+ }
+
+ /* Set a callback for the facility's level */
+ if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j],
+ NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE,
+ r_log_facility_change_cb,(void *)&(log_types[i].level[j])))
+ ABORT(r);
+ if(r=NR_reg_register_callback(log_types[i].dest_facility_key[j],
+ NR_REG_CB_ACTION_DELETE,
+ r_log_facility_delete_cb,(void *)&(log_types[i].level[j])))
+ ABORT(r);
+
+ }
+ }
+
+ _status=0;
+ abort:
+ if(_status)
+ RFREE(buf);
+ return(_status);
+ }
+
+int r_log_facility(int facility,char **typename)
+ {
+ if(facility >= 0 && facility < log_type_ct){
+ *typename=log_types[facility].facility_name;
+ return(0);
+ }
+ return(R_NOT_FOUND);
+ }
+
+static int r_log_get_reg_level(NR_registry name, int *out)
+ {
+ char level[32];
+ int r,_status;
+ int i;
+
+ if(r=NR_reg_get_string(name,level,sizeof(level)))
+ ABORT(r);
+
+ if(!strcasecmp(level,"none")){
+ *out=LOG_LEVEL_NONE;
+ return(0);
+ }
+
+ for(i=0;i<=LOG_DEBUG;i++){
+ if(!strcasecmp(level,log_level_reg_strings[i])){
+ *out=(int)i;
+ return(0);
+ }
+ }
+
+ if(i>LOG_DEBUG){
+ *out=LOG_LEVEL_UNDEFINED;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* Handle the case where a value changes */
+static void r_log_facility_change_cb(void *cb_arg, char action, NR_registry name)
+ {
+ int *lt_level=(int *)cb_arg;
+ int level;
+ int r,_status;
+
+ if(r=r_log_get_reg_level(name,&level))
+ ABORT(r);
+
+ *lt_level=level;
+
+ _status=0;
+ abort:
+ return;
+ }
+
+/* Handle the case where a value is deleted */
+static void r_log_facility_delete_cb(void *cb_arg, char action, NR_registry name)
+ {
+ int *lt_level=(int *)cb_arg;
+
+ *lt_level=LOG_LEVEL_UNDEFINED;
+ }
+
+int r_log(int facility,int level,const char *format,...)
+ {
+ va_list ap;
+
+ va_start(ap,format);
+
+ r_vlog(facility,level,format,ap);
+ va_end(ap);
+
+ return(0);
+ }
+
+int r_dump(int facility,int level,char *name,char *data,int len)
+ {
+ char *hex = 0;
+ int unused;
+
+ if(!r_logging(facility,level))
+ return(0);
+
+ hex=RMALLOC((len*2)+1);
+ if (!hex)
+ return(R_FAILED);
+
+ if (nr_nbin2hex((UCHAR*)data, len, hex, len*2+1, &unused))
+ strcpy(hex, "?");
+
+ if(name)
+ r_log(facility,level,"%s[%d]=%s",name,len,hex);
+ else
+ r_log(facility,level,"%s",hex);
+
+ RFREE(hex);
+ return(0);
+ }
+
+// Some platforms (notably WIN32) do not have this
+#ifndef va_copy
+ #ifdef WIN32
+ #define va_copy(dest, src) ( (dest) = (src) )
+ #else // WIN32
+ #error va_copy undefined, and semantics of assignment on va_list unknown
+ #endif //WIN32
+#endif //va_copy
+
+int r_vlog(int facility,int level,const char *format,va_list ap)
+ {
+ char log_fmt_buf[MAX_ERROR_STRING_SIZE];
+ char *level_str="unknown";
+ char *facility_str="unknown";
+ char *fmt_str=(char *)format;
+ int i;
+
+ if(r_log_env_verbose){
+ if((level>=LOG_EMERG) && (level<=LOG_DEBUG))
+ level_str=log_level_strings[level];
+
+ if(facility >= 0 && facility < log_type_ct)
+ facility_str=log_types[facility].facility_name;
+
+ snprintf(log_fmt_buf, MAX_ERROR_STRING_SIZE, "(%s/%s) %s",
+ facility_str,level_str,format);
+
+ log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
+ fmt_str=log_fmt_buf;
+ }
+
+ for(i=0; i<LOG_NUM_DESTINATIONS; i++){
+ if(r_logging_dest(i,facility,level)){
+ // Some platforms do not react well when you use a va_list more than
+ // once
+ va_list copy;
+ va_copy(copy, ap);
+ log_destinations[i].dest_vlog(facility,level,fmt_str,copy);
+ va_end(copy);
+ }
+ }
+ return(0);
+ }
+
+int stderr_vlog(int facility,int level,const char *format,va_list ap)
+ {
+#if 0 /* remove time stamping, for now */
+ char cbuf[30];
+ time_t tt;
+
+ tt=time(0);
+
+ ctime_r(&tt,cbuf);
+ cbuf[strlen(cbuf)-1]=0;
+
+ fprintf(stderr,"%s: ",cbuf);
+#endif
+
+ vfprintf(stderr,format,ap);
+ fprintf(stderr,"\n");
+ return(0);
+ }
+
+int syslog_vlog(int facility,int level,const char *format,va_list ap)
+ {
+#ifndef WIN32
+ vsyslog(level|LOG_LOCAL0,format,ap);
+#endif
+ return(0);
+ }
+
+int noop_vlog(int facility,int level,const char *format,va_list ap)
+ {
+ return(0);
+ }
+
+int r_log_e(int facility,int level,const char *format,...)
+ {
+ va_list ap;
+
+ va_start(ap,format);
+ r_vlog_e(facility,level,format,ap);
+ va_end(ap);
+
+ return(0);
+ }
+
+int r_vlog_e(int facility,int level,const char *format,va_list ap)
+ {
+ char log_fmt_buf[MAX_ERROR_STRING_SIZE];
+ if(r_logging(facility,level)) {
+ int formatlen = strlen(format);
+
+ if(formatlen+2 > MAX_ERROR_STRING_SIZE)
+ return(1);
+
+ strncpy(log_fmt_buf, format, formatlen);
+ strcpy(&log_fmt_buf[formatlen], ": ");
+ snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s",
+#ifdef WIN32
+ strerror(WSAGetLastError()));
+#else
+ strerror(errno));
+#endif
+ log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
+
+ r_vlog(facility,level,log_fmt_buf,ap);
+ }
+ return(0);
+ }
+
+int r_log_nr(int facility,int level,int r,const char *format,...)
+ {
+ va_list ap;
+
+ va_start(ap,format);
+ r_vlog_nr(facility,level,r,format,ap);
+ va_end(ap);
+
+ return(0);
+ }
+
+int r_vlog_nr(int facility,int level,int r,const char *format,va_list ap)
+ {
+ char log_fmt_buf[MAX_ERROR_STRING_SIZE];
+ if(r_logging(facility,level)) {
+ int formatlen = strlen(format);
+
+ if(formatlen+2 > MAX_ERROR_STRING_SIZE)
+ return(1);
+ strncpy(log_fmt_buf, format, formatlen);
+ strcpy(&log_fmt_buf[formatlen], ": ");
+ snprintf(&log_fmt_buf[formatlen+2], MAX_ERROR_STRING_SIZE - formatlen - 2, "%s",
+ nr_strerror(r));
+
+ log_fmt_buf[MAX_ERROR_STRING_SIZE-1]=0;
+
+ r_vlog(facility,level,log_fmt_buf,ap);
+ }
+ return(0);
+ }
+
+static int r_logging_dest(int dest_index, int facility, int level)
+ {
+ int thresh;
+
+ _r_log_init(0);
+
+ if(!log_destinations[dest_index].enabled)
+ return(0);
+
+ if(level <= r_log_level_environment)
+ return(1);
+
+ if(r_log_initted<R_LOG_INITTED2)
+ return(level<=r_log_level);
+
+ if(facility < 0 || facility > log_type_ct)
+ thresh=r_log_level;
+ else{
+ if(log_types[facility].level[dest_index]==LOG_LEVEL_NONE)
+ return(0);
+
+ if(log_types[facility].level[dest_index]>=0)
+ thresh=log_types[facility].level[dest_index];
+ else if(log_destinations[dest_index].default_level!=LOG_LEVEL_UNDEFINED)
+ thresh=log_destinations[dest_index].default_level;
+ else
+ thresh=r_log_level;
+ }
+
+ if(level<=thresh)
+ return(1);
+
+ return(0);
+ }
+
+int r_logging(int facility, int level)
+ {
+ int i;
+
+ _r_log_init(0);
+
+ /* return 1 if logging is on for any dest */
+
+ for(i=0; i<LOG_NUM_DESTINATIONS; i++){
+ if(r_logging_dest(i,facility,level))
+ return(1);
+ }
+
+ return(0);
+ }
+
+
+static int r_log_get_default_level(void)
+ {
+ char *log;
+ int _status;
+
+ log=getenv("R_LOG_LEVEL");
+
+ if(log){
+ r_log_level=atoi(log);
+ r_log_level_environment=atoi(log);
+ }
+ else{
+ r_log_level=LOGGING_DEFAULT_LEVEL;
+ }
+
+ _status=0;
+ //abort:
+ return(_status);
+ }
+
+
+static int r_log_get_destinations(int usereg)
+ {
+ char *log;
+ int i;
+ int r,_status;
+
+ log=getenv("R_LOG_DESTINATION");
+ if(log){
+ for(i=0; i<LOG_NUM_DESTINATIONS; i++)
+ log_destinations[i].enabled=!strcmp(log,log_destinations[i].dest_name);
+ }
+ else if(usereg){
+ NR_registry reg_key;
+ int i;
+ int value;
+ char c;
+
+ /* Get the data out of the registry */
+ for(i=0; i<LOG_NUM_DESTINATIONS; i++){
+ /* set callback for default level */
+ if(snprintf(reg_key,sizeof(reg_key),"%s.%s.level",LOGGING_REG_PREFIX,
+ log_destinations[i].dest_name)>=sizeof(reg_key))
+ ABORT(R_INTERNAL);
+
+ NR_reg_register_callback(reg_key,
+ NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE,
+ r_log_default_level_change_cb,0);
+
+ if(r=r_log_get_reg_level(reg_key,&value)){
+ if(r==R_NOT_FOUND)
+ log_destinations[i].default_level=LOG_LEVEL_UNDEFINED;
+ else
+ ABORT(R_INTERNAL);
+ }
+ else
+ log_destinations[i].default_level=value;
+
+ /* set callback for the enabled key for this logging dest */
+ if(snprintf(reg_key,sizeof(reg_key),"%s.%s.enabled",LOGGING_REG_PREFIX,
+ log_destinations[i].dest_name)>=sizeof(reg_key))
+ ABORT(R_INTERNAL);
+
+ NR_reg_register_callback(reg_key,
+ NR_REG_CB_ACTION_ADD|NR_REG_CB_ACTION_CHANGE|NR_REG_CB_ACTION_DELETE,
+ r_log_destination_change_cb,0);
+
+ if(r=NR_reg_get_char(reg_key,&c)){
+ if(r==R_NOT_FOUND)
+ log_destinations[i].enabled=0;
+ else
+ ABORT(r);
+ }
+ else
+ log_destinations[i].enabled=c;
+ }
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static void r_log_destination_change_cb(void *cb_arg, char action, NR_registry name)
+ {
+ r_log_get_destinations(1);
+ }
+
+static void r_log_default_level_change_cb(void *cb_arg, char action, NR_registry name)
+ {
+ r_log_get_destinations(1);
+ }
+
+
+int r_log_init()
+ {
+ _r_log_init(1);
+
+ return 0;
+ }
+
+int _r_log_init(int use_reg)
+ {
+#ifndef WIN32
+ char *log;
+#endif
+
+ if(!use_reg){
+ if(r_log_initted<R_LOG_INITTED1){
+ r_log_get_default_level();
+ r_log_get_destinations(0);
+
+ r_log_initted=R_LOG_INITTED1;
+ }
+ }
+ else{
+ if(r_log_initted<R_LOG_INITTED2){
+ int facility;
+
+ r_log_get_default_level();
+ r_log_get_destinations(1);
+
+ r_log_register("generic",&facility);
+ r_log_register("logging",&NR_LOG_LOGGING);
+
+ r_log_initted=R_LOG_INITTED2;
+ }
+ }
+
+#ifdef WIN32
+ r_log_env_verbose=1;
+#else
+ log=getenv("R_LOG_VERBOSE");
+ if(log)
+ r_log_env_verbose=atoi(log);
+#endif
+
+ return(0);
+ }
+
+int r_log_set_extra_destination(int default_level, r_dest_vlog *dest_vlog)
+ {
+ int i;
+ log_destination *dest = 0;
+
+ for(i=0; i<LOG_NUM_DESTINATIONS; i++){
+ if(!strcmp("extra",log_destinations[i].dest_name)){
+ dest=&log_destinations[i];
+ break;
+ }
+ }
+
+ if(!dest)
+ return(R_INTERNAL);
+
+ if (dest_vlog==0){
+ dest->enabled=0;
+ dest->dest_vlog=noop_vlog;
+ }
+ else{
+ dest->enabled=1;
+ dest->default_level=default_level;
+ dest->dest_vlog=dest_vlog;
+ }
+
+ return(0);
+ }
+
diff --git a/media/mtransport/third_party/nrappkit/src/log/r_log.h b/media/mtransport/third_party/nrappkit/src/log/r_log.h
new file mode 100644
index 000000000..a72dfa066
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/log/r_log.h
@@ -0,0 +1,85 @@
+/**
+ r_log.h
+
+
+ Copyright (C) 2001, RTFM, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Mon Dec 3 15:14:45 2001
+ */
+
+
+#ifndef _r_log_h
+#define _r_log_h
+
+#ifndef WIN32
+#include <syslog.h>
+#endif
+#include <stdarg.h>
+#include <r_common.h>
+
+int r_log(int facility,int level,const char *fmt,...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 3, 4)))
+#endif
+;
+
+int r_vlog(int facility,int level,const char *fmt,va_list ap);
+int r_dump(int facility,int level,char *name,char *data,int len);
+
+int r_log_e(int facility,int level,const char *fmt,...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 3, 4)))
+#endif
+;
+
+int r_vlog_e(int facility,int level,const char *fmt,va_list ap);
+int r_log_nr(int facility,int level,int r,const char *fmt,...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 4, 5)))
+#endif
+;
+
+int r_vlog_nr(int facility,int level,int r,const char *fmt,va_list ap);
+
+int r_log_register(char *tipename,int *facility);
+int r_log_facility(int facility,char **tipename);
+int r_logging(int facility, int level);
+int r_log_init(void);
+
+#define LOG_GENERIC 0
+#define LOG_COMMON 0
+
+typedef int r_dest_vlog(int facility,int level,const char *format,va_list ap);
+int r_log_set_extra_destination(int default_level, r_dest_vlog *dest_vlog);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/plugin/nr_plugin.h b/media/mtransport/third_party/nrappkit/src/plugin/nr_plugin.h
new file mode 100644
index 000000000..56da0624a
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/plugin/nr_plugin.h
@@ -0,0 +1,57 @@
+/**
+ nr_plugin.h
+
+
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@networkresonance.com Mon Jun 19 18:18:54 2006
+ */
+
+
+#ifndef _nr_plugin_h
+#define _nr_plugin_h
+
+typedef int (NR_plugin_hook)(void);
+
+typedef struct NR_plugin_hook_def_ {
+ char *type;
+ NR_plugin_hook *func;
+} NR_plugin_hook_def;
+
+typedef struct NR_plugin_def_ {
+ int api_version; // Should be 1
+ char *name;
+ char *version;
+ NR_plugin_hook_def *hooks;
+} NR_plugin_def;
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/port/android/include/android_funcs.h b/media/mtransport/third_party/nrappkit/src/port/android/include/android_funcs.h
new file mode 100644
index 000000000..d48bbe137
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/android/include/android_funcs.h
@@ -0,0 +1,62 @@
+/**
+ linux_funcs.h
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Mon Dec 13 16:28:22 2004
+ */
+
+
+#ifndef _android_funcs_h
+#define _android_funcs_h
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ETHERTYPE_VLAN 0x8100
+
+#define STDIO_BYTES_BUFFERED(fp) (fp->_IO_read_end - fp->_IO_read_ptr)
+
+size_t strlcat(char *dst, const char *src, size_t siz);
+#ifndef strlcpy
+#define strlcpy(a,b,c) \
+ (strncpy((a),(b),(c)), \
+ ((c)<= 0 ? 0 : ((a)[(c)-1]='\0')), \
+ strlen((b)))
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/port/android/include/csi_platform.h b/media/mtransport/third_party/nrappkit/src/port/android/include/csi_platform.h
new file mode 100644
index 000000000..bcfb2bce7
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/android/include/csi_platform.h
@@ -0,0 +1,55 @@
+/**
+ platform.h
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Mon Dec 13 17:26:51 2004
+ */
+
+
+#ifndef _platform_h
+#define _platform_h
+
+#include <android_funcs.h>
+
+#ifdef NR_SOCKET_IS_VOID_PTR
+typedef void* NR_SOCKET;
+#else
+typedef int NR_SOCKET;
+#define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count))
+#define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count))
+#define NR_SOCKET_CLOSE(sock) close(sock)
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/port/android/include/sys/ttycom.h b/media/mtransport/third_party/nrappkit/src/port/android/include/sys/ttycom.h
new file mode 100644
index 000000000..852bf9103
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/android/include/sys/ttycom.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef __ANDROID_TTYCOM_H
+#define __ANDROID_TTYCOM_H
+
+#include <asm/ioctls.h>
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/port/android/port-impl.mk b/media/mtransport/third_party/nrappkit/src/port/android/port-impl.mk
new file mode 100644
index 000000000..6704cfedf
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/android/port-impl.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2006, Network Resonance, Inc.
+# All Rights Reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Network Resonance, Inc. nor the name of any
+# contributors to this software may be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+#EXTATTR_IMPL=xattr
diff --git a/media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h b/media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
new file mode 100644
index 000000000..41f1a5b09
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
@@ -0,0 +1,57 @@
+/**
+ platform.h
+
+
+ Copyright (C) 2005, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ briank@network-resonance.com Tue Mar 15 14:28:12 PST 2005
+ */
+
+
+#ifndef _platform_h
+#define _platform_h
+
+#include <unistd.h>
+
+#define STDIO_BYTES_BUFFERED(fp) (fp->_r)
+
+#ifdef NR_SOCKET_IS_VOID_PTR
+typedef void* NR_SOCKET;
+#else
+typedef int NR_SOCKET;
+#define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count))
+#define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count))
+#define NR_SOCKET_CLOSE(sock) close(sock)
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/port/generic/include/sys/queue.h b/media/mtransport/third_party/nrappkit/src/port/generic/include/sys/queue.h
new file mode 100644
index 000000000..4d7b998a3
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/generic/include/sys/queue.h
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.58 2004/04/07 04:19:49 imp Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+#include <stddef.h>
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t)(&((type *)0)->field))
+#endif
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - - - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ *
+ */
+#define QUEUE_MACRO_DEBUG 0
+#if QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ char * lastfile;
+ int lastline;
+ char * prevfile;
+ int prevline;
+};
+
+#define TRACEBUF struct qm_trace trace;
+#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
+
+#define QMD_TRACE_HEAD(head) do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+} while (0)
+
+#define QMD_TRACE_ELEM(elem) do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+} while (0)
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define TRACEBUF
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_NEXT(curelm, field) = \
+ SLIST_NEXT(SLIST_NEXT(curelm, field), field); \
+ } \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *) \
+ ((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ if ((STAILQ_NEXT(curelm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+ } \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
+ if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+} while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT((elm)->field.tqe_next); \
+ TRASHIT((elm)->field.tqe_prev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+
+#ifdef _KERNEL
+
+/*
+ * XXX insque() and remque() are an old way of handling certain queues.
+ * They bogusly assumes that all queue heads look alike.
+ */
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+
+static __inline void
+insque(void *a, void *b)
+{
+ struct quehead *element = (struct quehead *)a,
+ *head = (struct quehead *)b;
+
+ element->qh_link = head->qh_link;
+ element->qh_rlink = head;
+ head->qh_link = element;
+ element->qh_link->qh_rlink = element;
+}
+
+static __inline void
+remque(void *a)
+{
+ struct quehead *element = (struct quehead *)a;
+
+ element->qh_link->qh_rlink = element->qh_rlink;
+ element->qh_rlink->qh_link = element->qh_link;
+ element->qh_rlink = 0;
+}
+
+#else /* !(__GNUC__ || __INTEL_COMPILER) */
+
+void insque(void *a, void *b);
+void remque(void *a);
+
+#endif /* __GNUC__ || __INTEL_COMPILER */
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h b/media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h
new file mode 100644
index 000000000..8aefaf924
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h
@@ -0,0 +1,55 @@
+/**
+ platform.h
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Mon Dec 13 17:26:51 2004
+ */
+
+
+#ifndef _platform_h
+#define _platform_h
+
+#include <linux_funcs.h>
+
+#ifdef NR_SOCKET_IS_VOID_PTR
+typedef void* NR_SOCKET;
+#else
+typedef int NR_SOCKET;
+#define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count))
+#define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count))
+#define NR_SOCKET_CLOSE(sock) close(sock)
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/port/linux/include/linux_funcs.h b/media/mtransport/third_party/nrappkit/src/port/linux/include/linux_funcs.h
new file mode 100644
index 000000000..161913630
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/linux/include/linux_funcs.h
@@ -0,0 +1,62 @@
+/**
+ linux_funcs.h
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Mon Dec 13 16:28:22 2004
+ */
+
+
+#ifndef _linux_funcs_h
+#define _linux_funcs_h
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ETHERTYPE_VLAN 0x8100
+
+#define STDIO_BYTES_BUFFERED(fp) (fp->_IO_read_end - fp->_IO_read_ptr)
+
+size_t strlcat(char *dst, const char *src, size_t siz);
+#ifndef strlcpy
+#define strlcpy(a,b,c) \
+ (strncpy((a),(b),(c)), \
+ ((c)<= 0 ? 0 : ((a)[(c)-1]='\0')), \
+ strlen((b)))
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h b/media/mtransport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h
new file mode 100644
index 000000000..6f8a3066d
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef __LINUX_TTYCOM_H
+#define __LINUX_TTYCOM_H
+
+#include <asm/ioctls.h>
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/port/linux/port-impl.mk b/media/mtransport/third_party/nrappkit/src/port/linux/port-impl.mk
new file mode 100644
index 000000000..6704cfedf
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/linux/port-impl.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2006, Network Resonance, Inc.
+# All Rights Reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of Network Resonance, Inc. nor the name of any
+# contributors to this software may be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+#EXTATTR_IMPL=xattr
diff --git a/media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h b/media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h
new file mode 100644
index 000000000..27c64c53d
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h
@@ -0,0 +1,107 @@
+/**
+ platform.h
+
+
+ Copyright (C) 2005, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ briank@network-resonance.com Tue Mar 15 14:28:12 PST 2005
+ */
+
+
+#ifndef _csi_platform_h
+#define _csi_platform_h
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400 // This prevents weird "'TryEnterCriticalSection': identifier not found"
+ // compiler errors when poco/win32_mutex.h is included
+#endif
+
+#define UINT8 UBLAH_IGNORE_ME_PLEASE
+#define INT8 BLAH_IGNORE_ME_PLEASE
+#include <winsock2.h>
+#undef UINT8
+#undef INT8
+#include <r_types.h>
+#include <errno.h>
+
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+
+#define strcasestr stristr
+
+/* Hack version of strlcpy (in util/util.c) */
+size_t strlcat(char *dst, const char *src, size_t siz);
+
+/* Hack version of getopt() (in util/getopt.c) */
+int getopt(int argc, char *argv[], char *opstring);
+extern char *optarg;
+extern int optind;
+extern int opterr;
+
+/* Hack version of gettimeofday() (in util/util.c) */
+int gettimeofday(struct timeval *tv, void *tz);
+
+#ifdef NR_SOCKET_IS_VOID_PTR
+typedef void* NR_SOCKET;
+#else
+typedef SOCKET NR_SOCKET;
+#define NR_SOCKET_READ(sock,buf,count) recv((sock),(buf),(count),0)
+#define NR_SOCKET_WRITE(sock,buf,count) send((sock),(buf),(count),0)
+#define NR_SOCKET_CLOSE(sock) closesocket(sock)
+#endif
+
+#ifndef EHOSTUNREACH
+#define EHOSTUNREACH WSAEHOSTUNREACH
+#endif
+
+#define LOG_EMERG 0
+#define LOG_ALERT 1
+#define LOG_CRIT 2
+#define LOG_ERR 3
+#define LOG_WARNING 4
+#define LOG_NOTICE 5
+#define LOG_INFO 6
+#define LOG_DEBUG 7
+
+// Until we refine the Windows port....
+
+#define in_addr_t UINT4
+
+#ifndef strlcpy
+#define strlcpy(a,b,c) \
+ (strncpy((a),(b),(c)), \
+ ((c)<= 0 ? 0 : ((a)[(c)-1]='\0')), \
+ strlen((b)))
+#endif
+
+#endif /* _csi_platform_h */
+
diff --git a/media/mtransport/third_party/nrappkit/src/registry/c2ru.c b/media/mtransport/third_party/nrappkit/src/registry/c2ru.c
new file mode 100644
index 000000000..f2c19cb80
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/c2ru.c
@@ -0,0 +1,320 @@
+/*
+ *
+ * c2ru.c
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/c2ru.c,v $
+ * $Revision: 1.3 $
+ * $Date: 2007/06/26 22:37:50 $
+ *
+ * c2r utility methods
+ *
+ *
+ * Copyright (C) 2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#include <sys/queue.h>
+#include <string.h>
+#include <registry.h>
+#include "nr_common.h"
+#include <r_errors.h>
+#include <r_macros.h>
+#include <ctype.h>
+#include "c2ru.h"
+
+
+#define NRGET(func, type, get) \
+int \
+func(NR_registry parent, char *child, type **out) \
+{ \
+ int r, _status; \
+ NR_registry registry; \
+ type tmp; \
+ \
+ if ((r = nr_c2ru_make_registry(parent, child, registry))) \
+ ABORT(r); \
+ \
+ if ((r = get(registry, &tmp))) { \
+ if (r != R_NOT_FOUND) \
+ ABORT(r); \
+ *out = 0; \
+ } \
+ else { \
+ *out = RCALLOC(sizeof(tmp)); \
+ if (*out == 0) \
+ ABORT(R_NO_MEMORY); \
+ **out = tmp; \
+ } \
+ \
+ _status = 0; \
+abort: \
+ return (_status); \
+}
+
+int
+nr_c2ru_get_char(NR_registry parent, char *child, char **out)
+{
+ int r, _status;
+ NR_registry registry;
+ char tmp;
+
+ if ((r = nr_c2ru_make_registry(parent, child, registry)))
+ ABORT(r);
+
+ if ((r = NR_reg_get_char(registry, &tmp))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ *out = 0;
+ }
+ else {
+ *out = RCALLOC(sizeof(tmp));
+ if (*out == 0)
+ ABORT(R_NO_MEMORY);
+ **out = tmp;
+ }
+
+ _status = 0;
+abort:
+ return (_status);
+}
+NRGET(nr_c2ru_get_uchar, UCHAR, NR_reg_get_uchar)
+NRGET(nr_c2ru_get_int2, INT2, NR_reg_get_int2)
+NRGET(nr_c2ru_get_uint2, UINT2, NR_reg_get_uint2)
+NRGET(nr_c2ru_get_int4, INT4, NR_reg_get_int4)
+NRGET(nr_c2ru_get_uint4, UINT4, NR_reg_get_uint4)
+NRGET(nr_c2ru_get_int8, INT8, NR_reg_get_int8)
+NRGET(nr_c2ru_get_uint8, UINT8, NR_reg_get_uint8)
+NRGET(nr_c2ru_get_double, double, NR_reg_get_double)
+NRGET(nr_c2ru_get_string, char*, NR_reg_alloc_string)
+NRGET(nr_c2ru_get_data, Data, NR_reg_alloc_data)
+
+
+#define NRSET(func, type, set) \
+int \
+func(NR_registry parent, char *child, type *in) \
+{ \
+ int r, _status; \
+ NR_registry registry; \
+ \
+ if (in == 0) \
+ return 0; \
+ \
+ if ((r = nr_c2ru_make_registry(parent, child, registry))) \
+ ABORT(r); \
+ \
+ if ((r = set(registry, *in))) \
+ ABORT(r); \
+ \
+ _status = 0; \
+abort: \
+ return (_status); \
+}
+
+NRSET(nr_c2ru_set_char, char, NR_reg_set_char)
+NRSET(nr_c2ru_set_uchar, UCHAR, NR_reg_set_uchar)
+NRSET(nr_c2ru_set_int2, INT2, NR_reg_set_int2)
+NRSET(nr_c2ru_set_uint2, UINT2, NR_reg_set_uint2)
+NRSET(nr_c2ru_set_int4, INT4, NR_reg_set_int4)
+NRSET(nr_c2ru_set_uint4, UINT4, NR_reg_set_uint4)
+NRSET(nr_c2ru_set_int8, INT8, NR_reg_set_int8)
+NRSET(nr_c2ru_set_uint8, UINT8, NR_reg_set_uint8)
+NRSET(nr_c2ru_set_double, double, NR_reg_set_double)
+NRSET(nr_c2ru_set_string, char*, NR_reg_set_string)
+
+int
+nr_c2ru_set_data(NR_registry parent, char *child, Data *in)
+{
+ int r, _status;
+ NR_registry registry;
+
+ if (in == 0)
+ return 0;
+
+ if ((r = nr_c2ru_make_registry(parent, child, registry)))
+ ABORT(r);
+
+ if ((r = NR_reg_set_bytes(registry, in->data, in->len)))
+ ABORT(r);
+
+ _status = 0;
+abort:
+ return (_status);
+}
+
+#define NRFREE(func, type) \
+int \
+func(type *in) \
+{ \
+ if (in) \
+ RFREE(in); \
+ return 0; \
+}
+
+NRFREE(nr_c2ru_free_char, char)
+NRFREE(nr_c2ru_free_uchar, UCHAR)
+NRFREE(nr_c2ru_free_int2, INT2)
+NRFREE(nr_c2ru_free_uint2, UINT2)
+NRFREE(nr_c2ru_free_int4, INT4)
+NRFREE(nr_c2ru_free_uint4, UINT4)
+NRFREE(nr_c2ru_free_int8, INT8)
+NRFREE(nr_c2ru_free_uint8, UINT8)
+NRFREE(nr_c2ru_free_double, double)
+
+
+int
+nr_c2ru_free_string(char **in)
+{
+ if (*in)
+ RFREE(*in);
+ if (in)
+ RFREE(in);
+ return 0;
+}
+
+int
+nr_c2ru_free_data(Data *in)
+{
+ int r, _status;
+
+ if (in) {
+ if ((r=r_data_destroy(&in)))
+ ABORT(r);
+ }
+
+ _status = 0;
+abort:
+ return (_status);
+}
+
+int
+nr_c2ru_get_children(NR_registry parent, char *child, void *ptr, size_t size, int (*get)(NR_registry, void*))
+{
+ int r, _status;
+ NR_registry registry;
+ unsigned int count;
+ int i;
+ NR_registry name;
+ struct entry { TAILQ_ENTRY(entry) entries; } *entry;
+ TAILQ_HEAD(, entry) *tailq = (void*)ptr;
+
+ TAILQ_INIT(tailq);
+
+ if ((r=nr_c2ru_make_registry(parent, child, registry)))
+ ABORT(r);
+
+ if ((r=NR_reg_get_child_count(registry, &count))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ }
+ else {
+ for (i = 0; i < count; ++i) {
+ if ((r=NR_reg_get_child_registry(registry, i, name))) {
+ /* ignore R_NOT_FOUND errors */
+ if (r == R_NOT_FOUND)
+ continue;
+ else
+ ABORT(r);
+ }
+
+ if ((r=get(name, &entry))) {
+ /* ignore R_NOT_FOUND errors */
+ if (r == R_NOT_FOUND)
+ continue;
+ else
+ ABORT(r);
+ }
+
+ TAILQ_INSERT_TAIL(tailq, entry, entries);
+ }
+ }
+
+ _status = 0;
+abort:
+ return (_status);
+}
+
+int
+nr_c2ru_set_children(NR_registry parent, char *child, void *ptr, int (*set)(NR_registry, void*), int (*label)(NR_registry, void*, char[NR_REG_MAX_NR_REGISTRY_LEN]))
+{
+ int r, _status;
+ NR_registry registry;
+ int i;
+ NR_registry name;
+ char buffer[NR_REG_MAX_NR_REGISTRY_LEN];
+ struct entry { TAILQ_ENTRY(entry) entries; } *entry;
+ TAILQ_HEAD(, entry) *tailq = (void*)ptr;
+
+ if ((r=nr_c2ru_make_registry(parent, child, registry)))
+ ABORT(r);
+
+ (void)NR_reg_del(registry);
+
+ i = 0;
+ TAILQ_FOREACH(entry, tailq, entries) {
+ if (label == 0 || (r=label(registry, entry, buffer))) {
+ snprintf(buffer, sizeof(buffer), "%d", i);
+ }
+ if ((r=nr_c2ru_make_registry(registry, buffer, name)))
+ ABORT(r);
+
+ if ((r=set(name, entry)))
+ ABORT(r);
+
+ ++i;
+ }
+
+ _status = 0;
+abort:
+ return (_status);
+}
+
+int
+nr_c2ru_free_children(void *ptr, int (*free)(void*))
+{
+ struct entry { TAILQ_ENTRY(entry) entries; } *entry;
+ TAILQ_HEAD(, entry) *tailq = (void*)ptr;
+
+ while (! TAILQ_EMPTY(tailq)) {
+ entry = TAILQ_FIRST(tailq);
+ TAILQ_REMOVE(tailq, entry, entries);
+ (void)free(entry);
+ }
+
+ return 0;
+}
+
+/* requires parent already in legal form */
+int
+nr_c2ru_make_registry(NR_registry parent, char *child, NR_registry out)
+{
+ return NR_reg_make_registry(parent, child, out);
+}
diff --git a/media/mtransport/third_party/nrappkit/src/registry/c2ru.h b/media/mtransport/third_party/nrappkit/src/registry/c2ru.h
new file mode 100644
index 000000000..0f8c38ecc
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/c2ru.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * c2ru.h
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/c2ru.h,v $
+ * $Revision: 1.2 $
+ * $Date: 2006/08/16 19:39:13 $
+ *
+ * c2r utility methods
+ *
+ *
+ * Copyright (C) 2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#ifndef __C2RU_H__
+#define __C2RU_H__
+
+#include <sys/types.h>
+#include <r_types.h>
+#include <r_data.h>
+#include "registry_int.h"
+
+int nr_c2ru_get_char(NR_registry parent, char *child, char **out);
+int nr_c2ru_get_uchar(NR_registry parent, char *child, UCHAR **out);
+int nr_c2ru_get_int2(NR_registry parent, char *child, INT2 **out);
+int nr_c2ru_get_uint2(NR_registry parent, char *child, UINT2 **out);
+int nr_c2ru_get_int4(NR_registry parent, char *child, INT4 **out);
+int nr_c2ru_get_uint4(NR_registry parent, char *child, UINT4 **out);
+int nr_c2ru_get_int8(NR_registry parent, char *child, INT8 **out);
+int nr_c2ru_get_uint8(NR_registry parent, char *child, UINT8 **out);
+int nr_c2ru_get_double(NR_registry parent, char *child, double **out);
+int nr_c2ru_get_data(NR_registry parent, char *child, Data **out);
+int nr_c2ru_get_string(NR_registry parent, char *child, char ***out);
+
+int nr_c2ru_set_char(NR_registry parent, char *child, char *data);
+int nr_c2ru_set_uchar(NR_registry parent, char *child, UCHAR *data);
+int nr_c2ru_set_int2(NR_registry parent, char *child, INT2 *data);
+int nr_c2ru_set_uint2(NR_registry parent, char *child, UINT2 *data);
+int nr_c2ru_set_int4(NR_registry parent, char *child, INT4 *data);
+int nr_c2ru_set_uint4(NR_registry parent, char *child, UINT4 *data);
+int nr_c2ru_set_int8(NR_registry parent, char *child, INT8 *data);
+int nr_c2ru_set_uint8(NR_registry parent, char *child, UINT8 *data);
+int nr_c2ru_set_double(NR_registry parent, char *child, double *data);
+int nr_c2ru_set_registry(NR_registry parent, char *child);
+int nr_c2ru_set_data(NR_registry parent, char *child, Data *data);
+int nr_c2ru_set_string(NR_registry parent, char *child, char **data);
+
+int nr_c2ru_free_char(char *data);
+int nr_c2ru_free_uchar(UCHAR *data);
+int nr_c2ru_free_int2(INT2 *data);
+int nr_c2ru_free_uint2(UINT2 *data);
+int nr_c2ru_free_int4(INT4 *data);
+int nr_c2ru_free_uint4(UINT4 *data);
+int nr_c2ru_free_int8(INT8 *data);
+int nr_c2ru_free_uint8(UINT8 *data);
+int nr_c2ru_free_double(double *data);
+int nr_c2ru_free_data(Data *data);
+int nr_c2ru_free_string(char **data);
+
+int nr_c2ru_get_children(NR_registry parent, char *child, void *ptr, size_t size, int (*get)(NR_registry, void*));
+int nr_c2ru_set_children(NR_registry parent, char *child, void *ptr, int (*set)(NR_registry, void*), int (*label)(NR_registry, void*, char[NR_REG_MAX_NR_REGISTRY_LEN]));
+int nr_c2ru_free_children(void *ptr, int (*free)(void*));
+
+int nr_c2ru_make_registry(NR_registry parent, char *child, NR_registry out);
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/registry/registry.c b/media/mtransport/third_party/nrappkit/src/registry/registry.c
new file mode 100644
index 000000000..d7ba472e5
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/registry.c
@@ -0,0 +1,604 @@
+/*
+ *
+ * registry.c
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry.c,v $
+ * $Revision: 1.6 $
+ * $Date: 2007/11/21 00:09:12 $
+ *
+ * Datastore for tracking configuration and related info.
+ *
+ *
+ * Copyright (C) 2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+#ifndef _MSC_VER
+#include <strings.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#endif
+#ifdef OPENSSL
+#include <openssl/ssl.h>
+#endif
+#include <ctype.h>
+#include "registry.h"
+#include "registry_int.h"
+#include "registry_vtbl.h"
+#include "r_assoc.h"
+#include "nr_common.h"
+#include "r_log.h"
+#include "r_errors.h"
+#include "r_macros.h"
+#include "c2ru.h"
+
+/* vtbl used to switch hit between local and remote invocations */
+static nr_registry_module *reg_vtbl = 0;
+
+/* must be in the order the types are numbered */
+static char *typenames[] = { "char", "UCHAR", "INT2", "UINT2", "INT4", "UINT4", "INT8", "UINT8", "double", "Data", "string", "registry" };
+
+int NR_LOG_REGISTRY=0;
+
+NR_registry NR_TOP_LEVEL_REGISTRY = "";
+
+int
+NR_reg_init(void *mode)
+{
+ int r, _status;
+ nr_registry_module *module = (nr_registry_module*)mode;
+#ifdef SANITY_CHECKS
+ NR_registry registry;
+#endif
+
+ if (reg_vtbl) {
+ if (reg_vtbl != module) {
+ r_log(LOG_GENERIC,LOG_ERR,"Can't reinitialize registry in different mode");
+ ABORT(R_INTERNAL);
+ }
+
+ return(0);
+ }
+
+ reg_vtbl = module;
+
+ if ((r=reg_vtbl->vtbl->init(mode)))
+ ABORT(r);
+
+#ifdef SANITY_CHECKS
+ if ((r=NR_reg_get_registry(NR_TOP_LEVEL_REGISTRY, registry)))
+ ABORT(r);
+ assert(strcmp(registry, NR_TOP_LEVEL_REGISTRY) == 0);
+#endif
+
+ r_log_init();
+ r_log_register("registry",&NR_LOG_REGISTRY);
+
+ _status=0;
+ abort:
+ r_log(NR_LOG_REGISTRY,
+ (_status ? LOG_ERR : LOG_INFO),
+ (_status ? "Couldn't initialize registry" : "Initialized registry"));
+ return(_status);
+}
+
+int
+NR_reg_initted(void)
+{
+ return reg_vtbl!=0;
+}
+
+#define NRREGGET(func, method, type) \
+int \
+func(NR_registry name, type *out) \
+{ \
+ return reg_vtbl->vtbl->method(name, out); \
+}
+
+NRREGGET(NR_reg_get_char, get_char, char)
+NRREGGET(NR_reg_get_uchar, get_uchar, UCHAR)
+NRREGGET(NR_reg_get_int2, get_int2, INT2)
+NRREGGET(NR_reg_get_uint2, get_uint2, UINT2)
+NRREGGET(NR_reg_get_int4, get_int4, INT4)
+NRREGGET(NR_reg_get_uint4, get_uint4, UINT4)
+NRREGGET(NR_reg_get_int8, get_int8, INT8)
+NRREGGET(NR_reg_get_uint8, get_uint8, UINT8)
+NRREGGET(NR_reg_get_double, get_double, double)
+
+int
+NR_reg_get_registry(NR_registry name, NR_registry out)
+{
+ return reg_vtbl->vtbl->get_registry(name, out);
+}
+
+int
+NR_reg_get_bytes(NR_registry name, UCHAR *out, size_t size, size_t *length)
+{
+ return reg_vtbl->vtbl->get_bytes(name, out, size, length);
+}
+
+int
+NR_reg_get_string(NR_registry name, char *out, size_t size)
+{
+ return reg_vtbl->vtbl->get_string(name, out, size);
+}
+
+int
+NR_reg_get_length(NR_registry name, size_t *length)
+{
+ return reg_vtbl->vtbl->get_length(name, length);
+}
+
+int
+NR_reg_get_type(NR_registry name, NR_registry_type type)
+{
+ return reg_vtbl->vtbl->get_type(name, type);
+}
+
+#define NRREGSET(func, method, type) \
+int \
+func(NR_registry name, type data) \
+{ \
+ return reg_vtbl->vtbl->method(name, data); \
+}
+
+NRREGSET(NR_reg_set_char, set_char, char)
+NRREGSET(NR_reg_set_uchar, set_uchar, UCHAR)
+NRREGSET(NR_reg_set_int2, set_int2, INT2)
+NRREGSET(NR_reg_set_uint2, set_uint2, UINT2)
+NRREGSET(NR_reg_set_int4, set_int4, INT4)
+NRREGSET(NR_reg_set_uint4, set_uint4, UINT4)
+NRREGSET(NR_reg_set_int8, set_int8, INT8)
+NRREGSET(NR_reg_set_uint8, set_uint8, UINT8)
+NRREGSET(NR_reg_set_double, set_double, double)
+NRREGSET(NR_reg_set_string, set_string, char*)
+
+int
+NR_reg_set_registry(NR_registry name)
+{
+ return reg_vtbl->vtbl->set_registry(name);
+}
+
+int
+NR_reg_set_bytes(NR_registry name, unsigned char *data, size_t length)
+{
+ return reg_vtbl->vtbl->set_bytes(name, data, length);
+}
+
+
+int
+NR_reg_del(NR_registry name)
+{
+ return reg_vtbl->vtbl->del(name);
+}
+
+int
+NR_reg_fin(NR_registry name)
+{
+ return reg_vtbl->vtbl->fin(name);
+}
+
+int
+NR_reg_get_child_count(char *parent, unsigned int *count)
+{
+ assert(sizeof(count) == sizeof(size_t));
+ return reg_vtbl->vtbl->get_child_count(parent, (size_t*)count);
+}
+
+int
+NR_reg_get_child_registry(char *parent, unsigned int i, NR_registry child)
+{
+ int r, _status;
+ size_t count;
+ NR_registry *children=0;
+
+ if ((r=reg_vtbl->vtbl->get_child_count(parent, &count)))
+ ABORT(r);
+
+ if (i >= count)
+ ABORT(R_NOT_FOUND);
+ else {
+ count++;
+ children = (NR_registry *)RCALLOC(count * sizeof(NR_registry));
+ if (!children)
+ ABORT(R_NO_MEMORY);
+
+ if ((r=reg_vtbl->vtbl->get_children(parent, children, count, &count)))
+ ABORT(r);
+
+ if (i >= count)
+ ABORT(R_NOT_FOUND);
+
+ strncpy(child, children[i], sizeof(NR_registry));
+ }
+
+ _status=0;
+ abort:
+ RFREE(children);
+ return(_status);
+}
+
+int
+NR_reg_get_children(NR_registry parent, NR_registry *children, size_t size, size_t *length)
+{
+ return reg_vtbl->vtbl->get_children(parent, children, size, length);
+}
+
+int
+NR_reg_dump()
+{
+ int r, _status;
+
+ if ((r=reg_vtbl->vtbl->dump(0)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+// convenience methods, call RFREE on the returned data
+int
+NR_reg_alloc_data(NR_registry name, Data *data)
+{
+ int r, _status;
+ size_t length;
+ UCHAR *tmp = 0;
+ size_t sanity_check;
+
+ if ((r=NR_reg_get_length(name, &length)))
+ ABORT(r);
+
+ if (!(tmp = (void*)RMALLOC(length)))
+ ABORT(R_NO_MEMORY);
+
+ if ((r=NR_reg_get_bytes(name, tmp, length, &sanity_check)))
+ ABORT(r);
+
+ assert(length == sanity_check);
+
+ data->len = length;
+ data->data = tmp;
+
+ _status=0;
+ abort:
+ if (_status) {
+ if (tmp) RFREE(tmp);
+ }
+ return(_status);
+}
+
+int
+NR_reg_alloc_string(NR_registry name, char **data)
+{
+ int r, _status;
+ size_t length;
+ char *tmp = 0;
+
+ if ((r=NR_reg_get_length(name, &length)))
+ ABORT(r);
+
+ if (!(tmp = (void*)RMALLOC(length+1)))
+ ABORT(R_NO_MEMORY);
+
+ if ((r=NR_reg_get_string(name, tmp, length+1)))
+ ABORT(r);
+
+ assert(length == strlen(tmp));
+
+ *data = tmp;
+
+ _status=0;
+ abort:
+ if (_status) {
+ if (tmp) RFREE(tmp);
+ }
+ return(_status);
+}
+
+
+char *
+nr_reg_type_name(int type)
+{
+ if ((type < NR_REG_TYPE_CHAR) || (type > NR_REG_TYPE_REGISTRY))
+ return(NULL);
+
+ return(typenames[type]);
+}
+
+int
+nr_reg_compute_type(char *typename, int *type)
+{
+ int _status;
+ int i;
+
+#ifdef SANITY_CHECKS
+ assert(!strcasecmp(typenames[NR_REG_TYPE_CHAR], "char"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_UCHAR], "UCHAR"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_INT2], "INT2"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_UINT2], "UINT2"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_INT4], "INT4"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_UINT4], "UINT4"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_INT8], "INT8"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_UINT8], "UINT8"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_DOUBLE], "double"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_BYTES], "Data"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_STRING], "string"));
+ assert(!strcasecmp(typenames[NR_REG_TYPE_REGISTRY], "registry"));
+ assert(sizeof(typenames)/sizeof(*typenames) == (NR_REG_TYPE_REGISTRY+1));
+#endif
+
+ for (i = 0; i < sizeof(typenames)/sizeof(*typenames); ++i) {
+ if (!strcasecmp(typenames[i], typename)) {
+ *type = i;
+ return 0;
+ }
+ }
+ ABORT(R_BAD_ARGS);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+/* More convenience functions: the same as their parents but they
+ take a prefix and a suffix */
+#define NRGET2(func, type, get) \
+int \
+func(NR_registry parent, char *child, type *out) \
+{ \
+ int r, _status; \
+ NR_registry registry; \
+ \
+ if ((r = NR_reg_make_registry(parent, child, registry))) \
+ ABORT(r); \
+ \
+ if ((r = get(registry, out))) { \
+ ABORT(r); \
+ } \
+ \
+ _status = 0; \
+abort: \
+ return (_status); \
+}
+
+NRGET2(NR_reg_get2_char, char, NR_reg_get_char)
+NRGET2(NR_reg_get2_uchar, UCHAR, NR_reg_get_uchar)
+NRGET2(NR_reg_get2_int2, INT2, NR_reg_get_int2)
+NRGET2(NR_reg_get2_uint2, UINT2, NR_reg_get_uint2)
+NRGET2(NR_reg_get2_int4, INT4, NR_reg_get_int4)
+NRGET2(NR_reg_get2_uint4, UINT4, NR_reg_get_uint4)
+NRGET2(NR_reg_get2_int8, INT8, NR_reg_get_int8)
+NRGET2(NR_reg_get2_uint8, UINT8, NR_reg_get_uint8)
+NRGET2(NR_reg_get2_double, double, NR_reg_get_double)
+NRGET2(NR_reg_alloc2_string, char*, NR_reg_alloc_string)
+NRGET2(NR_reg_alloc2_data, Data, NR_reg_alloc_data)
+
+int
+NR_reg_get2_bytes(NR_registry parent, char *child, UCHAR *out, size_t size, size_t *length)
+{
+ int r, _status;
+ NR_registry registry;
+
+ if ((r=NR_reg_make_registry(parent, child, registry)))
+ ABORT(r);
+
+ if ((r=NR_reg_get_bytes(registry, out, size, length)))
+ ABORT(r);
+
+ _status = 0;
+abort:
+ return (_status);
+}
+
+int
+NR_reg_get2_string(NR_registry parent, char *child, char *out, size_t size)
+{
+ int r, _status;
+ NR_registry registry;
+
+ if ((r=NR_reg_make_registry(parent, child, registry)))
+ ABORT(r);
+
+ if ((r=NR_reg_get_string(registry, out, size)))
+ ABORT(r);
+
+ _status = 0;
+abort:
+ return (_status);
+}
+
+/* More convenience functions: the same as their parents but they
+ take a prefix and a suffix */
+#define NRSET2(func, type, set) \
+int \
+func(NR_registry parent, char *child, type in) \
+{ \
+ int r, _status; \
+ NR_registry registry; \
+ \
+ if ((r = NR_reg_make_registry(parent, child, registry))) \
+ ABORT(r); \
+ \
+ if ((r = set(registry, in))) { \
+ ABORT(r); \
+ } \
+ \
+ _status = 0; \
+abort: \
+ return (_status); \
+}
+
+NRSET2(NR_reg_set2_char, char, NR_reg_set_char)
+NRSET2(NR_reg_set2_uchar, UCHAR, NR_reg_set_uchar)
+NRSET2(NR_reg_set2_int2, INT2, NR_reg_set_int2)
+NRSET2(NR_reg_set2_uint2, UINT2, NR_reg_set_uint2)
+NRSET2(NR_reg_set2_int4, INT4, NR_reg_set_int4)
+NRSET2(NR_reg_set2_uint4, UINT4, NR_reg_set_uint4)
+NRSET2(NR_reg_set2_int8, INT8, NR_reg_set_int8)
+NRSET2(NR_reg_set2_uint8, UINT8, NR_reg_set_uint8)
+NRSET2(NR_reg_set2_double, double, NR_reg_set_double)
+NRSET2(NR_reg_set2_string, char*, NR_reg_set_string)
+
+int
+NR_reg_set2_bytes(NR_registry prefix, char *name, UCHAR *data, size_t length)
+{
+ int r, _status;
+ NR_registry registry;
+
+ if ((r = NR_reg_make_registry(prefix, name, registry)))
+ ABORT(r);
+
+ if ((r = NR_reg_set_bytes(registry, data, length)))
+ ABORT(r);
+
+ _status = 0;
+abort:
+ return (_status);
+}
+
+
+int
+NR_reg_make_child_registry(NR_registry parent, NR_registry descendant, unsigned int generation, NR_registry child)
+{
+ int _status;
+ size_t length;
+
+ length = strlen(parent);
+
+ if (strncasecmp(parent, descendant, length))
+ ABORT(R_BAD_ARGS);
+
+ while (descendant[length] != '\0') {
+ if (descendant[length] == '.') {
+ if (generation == 0)
+ break;
+
+ --generation;
+ }
+
+ ++length;
+ if (length >= sizeof(NR_registry))
+ ABORT(R_BAD_ARGS);
+ }
+
+ strncpy(child, descendant, length);
+ child[length] = '\0';
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+NR_reg_get2_child_count(NR_registry base, NR_registry name, unsigned int *count)
+ {
+ int r, _status;
+ NR_registry registry;
+
+ if ((r=nr_c2ru_make_registry(base, name, registry)))
+ ABORT(r);
+
+ if (r=NR_reg_get_child_count(registry,count))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int
+NR_reg_get2_child_registry(NR_registry base, NR_registry name, unsigned int i, NR_registry child)
+ {
+ int r, _status;
+ NR_registry registry;
+
+ if ((r=nr_c2ru_make_registry(base, name, registry)))
+ ABORT(r);
+
+ if (r=NR_reg_get_child_registry(registry, i, child))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+/* requires parent already in legal form */
+int
+NR_reg_make_registry(NR_registry parent, char *child, NR_registry out)
+{
+ int r, _status;
+ int plen;
+ int clen;
+ char *c;
+ int i;
+
+ if ((r=nr_reg_is_valid(parent)))
+ ABORT(r);
+
+ if (*child == '.')
+ ABORT(R_BAD_ARGS);
+
+ clen = strlen(child);
+ if (!clen)
+ ABORT(R_BAD_ARGS);
+ plen = strlen(parent);
+ if ((plen + clen + 2) > sizeof(NR_registry))
+ ABORT(R_BAD_ARGS);
+
+ if (out != parent)
+ strcpy(out, parent);
+
+ c = &(out[plen]);
+
+ if (parent[0] != '\0') {
+ *c = '.';
+ ++c;
+ }
+
+ for (i = 0; i < clen; ++i, ++c) {
+ *c = child[i];
+ if (isspace(*c) || *c == '.' || *c == '/' || ! isprint(*c))
+ *c = '_';
+ }
+
+ *c = '\0';
+
+ _status = 0;
+abort:
+ return _status;
+}
+
diff --git a/media/mtransport/third_party/nrappkit/src/registry/registry.h b/media/mtransport/third_party/nrappkit/src/registry/registry.h
new file mode 100644
index 000000000..b48893ba7
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/registry.h
@@ -0,0 +1,154 @@
+/*
+ *
+ * registry.h
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry.h,v $
+ * $Revision: 1.3 $
+ * $Date: 2007/07/17 17:58:16 $
+ *
+ * Datastore for tracking configuration and related info.
+ *
+ *
+ * Copyright (C) 2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#ifndef __REGISTRY_H__
+#define __REGISTRY_H__
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <r_types.h>
+#include <r_data.h>
+
+#define NR_REG_MAX_NR_REGISTRY_LEN 128
+#define NR_REG_MAX_TYPE_LEN 32
+
+typedef char NR_registry[NR_REG_MAX_NR_REGISTRY_LEN];
+typedef char NR_registry_type[NR_REG_MAX_TYPE_LEN];
+
+extern NR_registry NR_TOP_LEVEL_REGISTRY;
+
+extern void *NR_REG_MODE_LOCAL;
+extern void *NR_REG_MODE_REMOTE;
+
+int NR_reg_init(void *mode);
+
+int NR_reg_initted(void);
+
+int NR_reg_get_char(NR_registry name, char *out);
+int NR_reg_get_uchar(NR_registry name, UCHAR *out);
+int NR_reg_get_int2(NR_registry name, INT2 *out);
+int NR_reg_get_uint2(NR_registry name, UINT2 *out);
+int NR_reg_get_int4(NR_registry name, INT4 *out);
+int NR_reg_get_uint4(NR_registry name, UINT4 *out);
+int NR_reg_get_int8(NR_registry name, INT8 *out);
+int NR_reg_get_uint8(NR_registry name, UINT8 *out);
+int NR_reg_get_double(NR_registry name, double *out);
+int NR_reg_get_registry(NR_registry name, NR_registry out);
+
+int NR_reg_get_bytes(NR_registry name, UCHAR *out, size_t size, size_t *length);
+int NR_reg_get_string(NR_registry name, char *out, size_t size);
+int NR_reg_get_length(NR_registry name, size_t *length);
+int NR_reg_get_type(NR_registry name, NR_registry_type type);
+
+
+int NR_reg_get2_char(NR_registry prefix, char *name, char *);
+int NR_reg_get2_uchar(NR_registry prefix, char *name, UCHAR *);
+int NR_reg_get2_int2(NR_registry prefix, char *name, INT2 *);
+int NR_reg_get2_uint2(NR_registry prefix, char *name, UINT2 *);
+int NR_reg_get2_int4(NR_registry prefix, char *name, INT4 *);
+int NR_reg_get2_uint4(NR_registry prefix, char *name, UINT4 *);
+int NR_reg_get2_int8(NR_registry prefix, char *name, INT8 *);
+int NR_reg_get2_uint8(NR_registry prefix, char *name, UINT8 *);
+int NR_reg_get2_double(NR_registry prefix, char *name, double *);
+int NR_reg_get2_bytes(NR_registry prefix, char *name, UCHAR *out, size_t size, size_t *length);
+int NR_reg_get2_string(NR_registry prefix, char *name, char *out, size_t size);
+
+int NR_reg_alloc2_string(NR_registry prefix, char *name, char **);
+int NR_reg_alloc2_data(NR_registry prefix, char *name, Data *);
+
+int NR_reg_set_char(NR_registry name, char data);
+int NR_reg_set_uchar(NR_registry name, UCHAR data);
+int NR_reg_set_int2(NR_registry name, INT2 data);
+int NR_reg_set_uint2(NR_registry name, UINT2 data);
+int NR_reg_set_int4(NR_registry name, INT4 data);
+int NR_reg_set_uint4(NR_registry name, UINT4 data);
+int NR_reg_set_int8(NR_registry name, INT8 data);
+int NR_reg_set_uint8(NR_registry name, UINT8 data);
+int NR_reg_set_double(NR_registry name, double data);
+
+int NR_reg_set_registry(NR_registry name);
+
+int NR_reg_set_bytes(NR_registry name, UCHAR *data, size_t length);
+int NR_reg_set_string(NR_registry name, char *data);
+
+int NR_reg_set2_char(NR_registry prefix, char *name, char data);
+int NR_reg_set2_uchar(NR_registry prefix, char *name, UCHAR data);
+int NR_reg_set2_int2(NR_registry prefix, char *name, INT2 data);
+int NR_reg_set2_uint2(NR_registry prefix, char *name, UINT2 data);
+int NR_reg_set2_int4(NR_registry prefix, char *name, INT4 data);
+int NR_reg_set2_uint4(NR_registry prefix, char *name, UINT4 data);
+int NR_reg_set2_int8(NR_registry prefix, char *name, INT8 data);
+int NR_reg_set2_uint8(NR_registry prefix, char *name, UINT8 data);
+int NR_reg_set2_double(NR_registry prefix, char *name, double data);
+
+int NR_reg_set2_bytes(NR_registry prefix, char *name, UCHAR *data, size_t length);
+int NR_reg_set2_string(NR_registry prefix, char *name, char *data);
+
+int NR_reg_del(NR_registry name);
+
+int NR_reg_fin(NR_registry name);
+
+int NR_reg_get_child_count(NR_registry parent, unsigned int *count);
+int NR_reg_get_child_registry(NR_registry parent, unsigned int i, NR_registry child);
+int NR_reg_get2_child_count(NR_registry base, NR_registry name, unsigned int *count);
+int NR_reg_get2_child_registry(NR_registry base, NR_registry name, unsigned int i, NR_registry child);
+int NR_reg_get_children(NR_registry parent, NR_registry children[], size_t size, size_t *length);
+
+int NR_reg_dump(void);
+
+/* convenience methods, call RFREE on the returned data */
+int NR_reg_alloc_data(NR_registry name, Data *data);
+int NR_reg_alloc_string(NR_registry name, char **data);
+
+#define NR_REG_CB_ACTION_ADD (1<<0)
+#define NR_REG_CB_ACTION_CHANGE (1<<1)
+#define NR_REG_CB_ACTION_DELETE (1<<2)
+#define NR_REG_CB_ACTION_FINAL (1<<6)
+int NR_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg);
+int NR_reg_unregister_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name));
+
+int NR_reg_make_registry(NR_registry parent, char *child, NR_registry out);
+int NR_reg_make_child_registry(NR_registry parent, NR_registry descendant, unsigned int generation, NR_registry child);
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/registry/registry_int.h b/media/mtransport/third_party/nrappkit/src/registry/registry_int.h
new file mode 100644
index 000000000..d6d412c54
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/registry_int.h
@@ -0,0 +1,97 @@
+#if 0
+#define NR_LOG_REGISTRY BLAHBLAH()
+#define LOG_REGISTRY BLAHBLAH()
+static int BLAHBLAH() {
+int blahblah;
+r_log_register("registry",&blahblah);
+return blahblah;
+}
+#endif
+
+/*
+ *
+ * registry_int.h
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry_int.h,v $
+ * $Revision: 1.3 $
+ * $Date: 2007/06/26 22:37:51 $
+ *
+ * Callback-related functions
+ *
+ *
+ * Copyright (C) 2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#ifndef __REGISTRY_INT_H__
+#define __REGISTRY_INT_H__
+
+#include <sys/types.h>
+#include <r_types.h>
+#ifndef NO_REG_RPC
+#include <rpc/rpc.h>
+#endif
+
+extern int NR_LOG_REGISTRY;
+
+int nr_reg_is_valid(NR_registry name);
+
+#define NR_REG_TYPE_CHAR 0
+#define NR_REG_TYPE_UCHAR 1
+#define NR_REG_TYPE_INT2 2
+#define NR_REG_TYPE_UINT2 3
+#define NR_REG_TYPE_INT4 4
+#define NR_REG_TYPE_UINT4 5
+#define NR_REG_TYPE_INT8 6
+#define NR_REG_TYPE_UINT8 7
+#define NR_REG_TYPE_DOUBLE 8
+#define NR_REG_TYPE_BYTES 9
+#define NR_REG_TYPE_STRING 10
+#define NR_REG_TYPE_REGISTRY 11
+char *nr_reg_type_name(int type);
+int nr_reg_compute_type(char *type_name, int *type);
+
+char *nr_reg_action_name(int action);
+
+int nr_reg_cb_init(void);
+int nr_reg_client_cb_init(void);
+int nr_reg_register_for_callbacks(int fd, int connect_to_port);
+int nr_reg_raise_event(NR_registry name, int action);
+#ifndef NO_REG_RPC
+int nr_reg_get_client(CLIENT **client);
+#endif
+
+#define CALLBACK_SERVER_ADDR "127.0.0.1"
+#define CALLBACK_SERVER_PORT 8082
+#define CALLBACK_SERVER_BACKLOG 32
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/registry/registry_local.c b/media/mtransport/third_party/nrappkit/src/registry/registry_local.c
new file mode 100644
index 000000000..c52e22351
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/registry_local.c
@@ -0,0 +1,1168 @@
+/*
+ *
+ * registry.c
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry_local.c,v $
+ * $Revision: 1.4 $
+ * $Date: 2007/11/21 00:09:13 $
+ *
+ * Datastore for tracking configuration and related info.
+ *
+ *
+ * Copyright (C) 2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+#ifndef WIN32
+#include <strings.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#endif
+#ifdef OPENSSL
+#include <openssl/ssl.h>
+#endif
+#include <ctype.h>
+#include "registry.h"
+#include "registry_int.h"
+#include "registry_vtbl.h"
+#include "r_assoc.h"
+#include "nr_common.h"
+#include "r_log.h"
+#include "r_errors.h"
+#include "r_macros.h"
+
+/* if C were an object-oriented language, nr_scalar_registry_node and
+ * nr_array_registry_node would subclass nr_registry_node, but it isn't
+ * object-oriented language, so this is used in cases where the pointer
+ * could be of either type */
+typedef struct nr_registry_node_ {
+ unsigned char type;
+} nr_registry_node;
+
+typedef struct nr_scalar_registry_node_ {
+ unsigned char type;
+ union {
+ char _char;
+ UCHAR _uchar;
+ INT2 _nr_int2;
+ UINT2 _nr_uint2;
+ INT4 _nr_int4;
+ UINT4 _nr_uint4;
+ INT8 _nr_int8;
+ UINT8 _nr_uint8;
+ double _double;
+ } scalar;
+} nr_scalar_registry_node;
+
+/* string, bytes */
+typedef struct nr_array_registry_node_ {
+ unsigned char type;
+ struct {
+ unsigned int length;
+ unsigned char data[1];
+ } array;
+} nr_array_registry_node;
+
+static int nr_reg_local_init(nr_registry_module *me);
+static int nr_reg_local_get_char(NR_registry name, char *data);
+static int nr_reg_local_get_uchar(NR_registry name, UCHAR *data);
+static int nr_reg_local_get_int2(NR_registry name, INT2 *data);
+static int nr_reg_local_get_uint2(NR_registry name, UINT2 *data);
+static int nr_reg_local_get_int4(NR_registry name, INT4 *data);
+static int nr_reg_local_get_uint4(NR_registry name, UINT4 *data);
+static int nr_reg_local_get_int8(NR_registry name, INT8 *data);
+static int nr_reg_local_get_uint8(NR_registry name, UINT8 *data);
+static int nr_reg_local_get_double(NR_registry name, double *data);
+static int nr_reg_local_get_registry(NR_registry name, NR_registry data);
+static int nr_reg_local_get_bytes(NR_registry name, UCHAR *data, size_t size, size_t *length);
+static int nr_reg_local_get_string(NR_registry name, char *data, size_t size);
+static int nr_reg_local_get_length(NR_registry name, size_t *len);
+static int nr_reg_local_get_type(NR_registry name, NR_registry_type type);
+static int nr_reg_local_set_char(NR_registry name, char data);
+static int nr_reg_local_set_uchar(NR_registry name, UCHAR data);
+static int nr_reg_local_set_int2(NR_registry name, INT2 data);
+static int nr_reg_local_set_uint2(NR_registry name, UINT2 data);
+static int nr_reg_local_set_int4(NR_registry name, INT4 data);
+static int nr_reg_local_set_uint4(NR_registry name, UINT4 data);
+static int nr_reg_local_set_int8(NR_registry name, INT8 data);
+static int nr_reg_local_set_uint8(NR_registry name, UINT8 data);
+static int nr_reg_local_set_double(NR_registry name, double data);
+static int nr_reg_local_set_registry(NR_registry name);
+static int nr_reg_local_set_bytes(NR_registry name, UCHAR *data, size_t length);
+static int nr_reg_local_set_string(NR_registry name, char *data);
+static int nr_reg_local_del(NR_registry name);
+static int nr_reg_local_get_child_count(NR_registry parent, size_t *count);
+static int nr_reg_local_get_children(NR_registry parent, NR_registry *data, size_t size, size_t *length);
+static int nr_reg_local_fin(NR_registry name);
+static int nr_reg_local_dump(int sorted);
+static int nr_reg_insert_node(char *name, void *node);
+static int nr_reg_change_node(char *name, void *node, void *old);
+static int nr_reg_get(char *name, int type, void *out);
+static int nr_reg_get_data(NR_registry name, nr_scalar_registry_node *node, void *out);
+static int nr_reg_get_array(char *name, unsigned char type, UCHAR *out, size_t size, size_t *length);
+static int nr_reg_set(char *name, int type, void *data);
+static int nr_reg_set_array(char *name, unsigned char type, UCHAR *data, size_t length);
+static int nr_reg_set_parent_registries(char *name);
+
+/* make these static OLD_REGISTRY */
+#if 0
+static int nr_reg_fetch_node(char *name, unsigned char type, nr_registry_node **node, int *free_node);
+static char *nr_reg_alloc_node_data(char *name, nr_registry_node *node, int *freeit);
+#else
+int nr_reg_fetch_node(char *name, unsigned char type, nr_registry_node **node, int *free_node);
+char *nr_reg_alloc_node_data(char *name, nr_registry_node *node, int *freeit);
+#endif
+static int nr_reg_rfree(void *ptr);
+#if 0 /* Unused currently */
+static int nr_reg_noop(void *ptr);
+#endif
+static int nr_reg_compute_length(char *name, nr_registry_node *node, size_t *length);
+char *nr_reg_action_name(int action);
+
+/* the registry, containing mappings like "foo.bar.baz" to registry
+ * nodes, which are either of type nr_scalar_registry_node or
+ * nr_array_registry_node */
+static r_assoc *nr_registry = 0;
+
+#if 0 /* Unused currently */
+static nr_array_registry_node nr_top_level_node;
+#endif
+
+typedef struct nr_reg_find_children_arg_ {
+ size_t size;
+ NR_registry *children;
+ size_t length;
+} nr_reg_find_children_arg;
+
+static int nr_reg_local_iter(char *prefix, int (*action)(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node), void *ptr);
+static int nr_reg_local_iter_delete(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node);
+static int nr_reg_local_find_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node);
+static int nr_reg_local_count_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node);
+static int nr_reg_local_dump_print(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node);
+
+
+
+int
+nr_reg_local_iter(NR_registry prefix, int (*action)(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node), void *ptr)
+{
+ int r, _status;
+ r_assoc_iterator iter;
+ char *name;
+ int namel;
+ nr_registry_node *node;
+ int prefixl;
+
+ if (prefix == 0)
+ ABORT(R_INTERNAL);
+
+ if ((r=r_assoc_init_iter(nr_registry, &iter)))
+ ABORT(r);
+
+ prefixl = strlen(prefix);
+
+ for (;;) {
+ if ((r=r_assoc_iter(&iter, (void*)&name, &namel, (void*)&node))) {
+ if (r == R_EOD)
+ break;
+ else
+ ABORT(r);
+ }
+
+ /* subtract to remove the '\0' character from the string length */
+ --namel;
+
+ /* sanity check that the name is null-terminated */
+ assert(namel >= 0);
+ assert(name[namel] == '\0');
+
+ if (namel < 0 || name[namel] != '\0' || node == 0)
+ break;
+
+ /* 3 cases where action will be called:
+ * 1) prefix == ""
+ * 2) prefix == name
+ * 3) name == prefix + '.'
+ */
+ if (prefixl == 0
+ || ((namel == prefixl || (namel > prefixl && name[prefixl] == '.'))
+ && !strncmp(prefix, name, prefixl))) {
+ if ((r=action(ptr, &iter, prefix, name, node)))
+ ABORT(r);
+ }
+ }
+
+ _status=0;
+ abort:
+
+ return(_status);
+}
+
+int
+nr_reg_local_iter_delete(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node)
+{
+ int r, _status;
+
+ if ((r=r_assoc_iter_delete(iter)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_local_find_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node)
+{
+ int _status;
+ int prefixl = strlen(prefix);
+ char *dot;
+ nr_reg_find_children_arg *arg = (void*)ptr;
+
+ assert(sizeof(*(arg->children)) == sizeof(NR_registry));
+
+ /* only grovel through immediate children */
+ if (prefixl == 0 || name[prefixl] == '.') {
+ if (name[prefixl] != '\0') {
+ dot = strchr(&name[prefixl+1], '.');
+ if (dot == 0) {
+ strncpy(arg->children[arg->length], name, sizeof(NR_registry)-1);
+ ++arg->length;
+
+ /* only grab as many as there are room for */
+ if (arg->length >= arg->size)
+ ABORT(R_INTERRUPTED);
+ }
+ }
+ }
+
+ _status = 0;
+ abort:
+ return _status;
+}
+
+int
+nr_reg_local_count_children(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node)
+{
+ int prefixl = strlen(prefix);
+ char *dot;
+
+ /* only count children */
+ if (name[prefixl] == '.') {
+ dot = strchr(&name[prefixl+1], '.');
+ if (dot == 0)
+ ++(*(unsigned int *)ptr);
+ }
+ else if (name[0] != '\0') {
+ if (prefixl == 0)
+ ++(*(unsigned int *)ptr);
+ }
+
+ return 0;
+}
+
+int
+nr_reg_local_dump_print(void *ptr, r_assoc_iterator *iter, char *prefix, char *name, nr_registry_node *node)
+{
+ int _status;
+ int freeit = 0;
+ char *data;
+
+ /* only print leaf nodes */
+ if (node->type != NR_REG_TYPE_REGISTRY) {
+ data = nr_reg_alloc_node_data(name, node, &freeit);
+ if (ptr)
+ fprintf((FILE*)ptr, "%s: %s\n", name, data);
+ else
+ r_log(NR_LOG_REGISTRY, LOG_INFO, "%s: %s", name, data);
+ if (freeit)
+ RFREE(data);
+ }
+
+ _status=0;
+ //abort:
+ return(_status);
+}
+
+
+#if 0 /* Unused currently */
+int
+nr_reg_noop(void *ptr)
+{
+ return 0;
+}
+#endif
+
+int
+nr_reg_rfree(void *ptr)
+{
+ RFREE(ptr);
+ return 0;
+}
+
+int
+nr_reg_fetch_node(char *name, unsigned char type, nr_registry_node **node, int *free_node)
+{
+ int r, _status;
+
+ *node = 0;
+ *free_node = 0;
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ if ((r=r_assoc_fetch(nr_registry, name, strlen(name)+1, (void*)node)))
+ ABORT(r);
+
+ if ((*node)->type != type)
+ ABORT(R_FAILED);
+
+ _status=0;
+ abort:
+ if (_status) {
+ if (*node)
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't fetch node '%s' ('%s'), found '%s' instead",
+ name, nr_reg_type_name(type), nr_reg_type_name((*node)->type));
+ else
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't fetch node '%s' ('%s')",
+ name, nr_reg_type_name(type));
+ }
+ else {
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Fetched node '%s' ('%s')",
+ name, nr_reg_type_name(type));
+ }
+ return(_status);
+}
+
+int
+nr_reg_insert_node(char *name, void *node)
+{
+ int r, _status;
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ /* since the registry application is not multi-threaded, a node being
+ * inserted should always be a new node because the registry app must
+ * have looked for a node with this key but not found it, so it is
+ * being created/inserted now using R_ASSOC_NEW */
+ if ((r=r_assoc_insert(nr_registry, name, strlen(name)+1, node, 0, nr_reg_rfree, R_ASSOC_NEW)))
+ ABORT(r);
+
+ if ((r=nr_reg_set_parent_registries(name)))
+ ABORT(r);
+
+ if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_ADD)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if (r_logging(NR_LOG_REGISTRY, LOG_INFO)) {
+ int freeit;
+ char *data = nr_reg_alloc_node_data(name, (void*)node, &freeit);
+ r_log(NR_LOG_REGISTRY, LOG_INFO,
+ "insert '%s' (%s) %s: %s", name,
+ nr_reg_type_name(((nr_registry_node*)node)->type),
+ (_status ? "FAILED" : "succeeded"), data);
+ if (freeit)
+ RFREE(data);
+ }
+ return(_status);
+}
+
+int
+nr_reg_change_node(char *name, void *node, void *old)
+{
+ int r, _status;
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ if (old != node) {
+ if ((r=r_assoc_insert(nr_registry, name, strlen(name)+1, node, 0, nr_reg_rfree, R_ASSOC_REPLACE)))
+ ABORT(r);
+ }
+
+ if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_CHANGE)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if (r_logging(NR_LOG_REGISTRY, LOG_INFO)) {
+ int freeit;
+ char *data = nr_reg_alloc_node_data(name, (void*)node, &freeit);
+ r_log(NR_LOG_REGISTRY, LOG_INFO,
+ "change '%s' (%s) %s: %s", name,
+ nr_reg_type_name(((nr_registry_node*)node)->type),
+ (_status ? "FAILED" : "succeeded"), data);
+ if (freeit)
+ RFREE(data);
+ }
+ return(_status);
+}
+
+char *
+nr_reg_alloc_node_data(char *name, nr_registry_node *node, int *freeit)
+{
+ char *s = 0;
+ int len;
+ int alloc = 0;
+ unsigned int i;
+
+ *freeit = 0;
+
+ switch (node->type) {
+ default:
+ alloc = 100; /* plenty of room for any of the scalar types */
+ break;
+ case NR_REG_TYPE_REGISTRY:
+ alloc = strlen(name) + 1;
+ break;
+ case NR_REG_TYPE_BYTES:
+ alloc = (2 * ((nr_array_registry_node*)node)->array.length) + 1;
+ break;
+ case NR_REG_TYPE_STRING:
+ alloc = 0;
+ break;
+ }
+
+ if (alloc > 0) {
+ s = (void*)RMALLOC(alloc);
+ if (!s)
+ return "";
+
+ *freeit = 1;
+ }
+
+ len = alloc;
+
+ switch (node->type) {
+ case NR_REG_TYPE_CHAR:
+ i = ((nr_scalar_registry_node*)node)->scalar._char;
+ if (isprint(i) && ! isspace(i))
+ snprintf(s, len, "%c", (char)i);
+ else
+ snprintf(s, len, "\\%03o", (char)i);
+ break;
+ case NR_REG_TYPE_UCHAR:
+ snprintf(s, len, "0x%02x", ((nr_scalar_registry_node*)node)->scalar._uchar);
+ break;
+ case NR_REG_TYPE_INT2:
+ snprintf(s, len, "%d", ((nr_scalar_registry_node*)node)->scalar._nr_int2);
+ break;
+ case NR_REG_TYPE_UINT2:
+ snprintf(s, len, "%u", ((nr_scalar_registry_node*)node)->scalar._nr_uint2);
+ break;
+ case NR_REG_TYPE_INT4:
+ snprintf(s, len, "%d", ((nr_scalar_registry_node*)node)->scalar._nr_int4);
+ break;
+ case NR_REG_TYPE_UINT4:
+ snprintf(s, len, "%u", ((nr_scalar_registry_node*)node)->scalar._nr_uint4);
+ break;
+ case NR_REG_TYPE_INT8:
+ snprintf(s, len, "%lld", ((nr_scalar_registry_node*)node)->scalar._nr_int8);
+ break;
+ case NR_REG_TYPE_UINT8:
+ snprintf(s, len, "%llu", ((nr_scalar_registry_node*)node)->scalar._nr_uint8);
+ break;
+ case NR_REG_TYPE_DOUBLE:
+ snprintf(s, len, "%#f", ((nr_scalar_registry_node*)node)->scalar._double);
+ break;
+ case NR_REG_TYPE_REGISTRY:
+ snprintf(s, len, "%s", name);
+ break;
+ case NR_REG_TYPE_BYTES:
+ for (i = 0; i < ((nr_array_registry_node*)node)->array.length; ++i) {
+ sprintf(&s[2*i], "%02x", ((nr_array_registry_node*)node)->array.data[i]);
+ }
+ break;
+ case NR_REG_TYPE_STRING:
+ s = (char*)((nr_array_registry_node*)node)->array.data;
+ break;
+ default:
+ assert(0); /* bad value */
+ *freeit = 0;
+ s = "";
+ break;
+ }
+
+ return s;
+}
+
+int
+nr_reg_get(char *name, int type, void *out)
+{
+ int r, _status;
+ nr_scalar_registry_node *node = 0;
+ int free_node = 0;
+
+ if ((r=nr_reg_fetch_node(name, type, (void*)&node, &free_node)))
+ ABORT(r);
+
+ if ((r=nr_reg_get_data(name, node, out)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if (free_node) RFREE(node);
+ return(_status);
+}
+
+int
+nr_reg_get_data(NR_registry name, nr_scalar_registry_node *node, void *out)
+{
+ int _status;
+
+ switch (node->type) {
+ case NR_REG_TYPE_CHAR:
+ *(char*)out = node->scalar._char;
+ break;
+ case NR_REG_TYPE_UCHAR:
+ *(UCHAR*)out = node->scalar._uchar;
+ break;
+ case NR_REG_TYPE_INT2:
+ *(INT2*)out = node->scalar._nr_int2;
+ break;
+ case NR_REG_TYPE_UINT2:
+ *(UINT2*)out = node->scalar._nr_uint2;
+ break;
+ case NR_REG_TYPE_INT4:
+ *(INT4*)out = node->scalar._nr_int4;
+ break;
+ case NR_REG_TYPE_UINT4:
+ *(UINT4*)out = node->scalar._nr_uint4;
+ break;
+ case NR_REG_TYPE_INT8:
+ *(INT8*)out = node->scalar._nr_int8;
+ break;
+ case NR_REG_TYPE_UINT8:
+ *(UINT8*)out = node->scalar._nr_uint8;
+ break;
+ case NR_REG_TYPE_DOUBLE:
+ *(double*)out = node->scalar._double;
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_get_array(char *name, unsigned char type, unsigned char *out, size_t size, size_t *length)
+{
+ int r, _status;
+ nr_array_registry_node *node = 0;
+ int free_node = 0;
+
+ if ((r=nr_reg_fetch_node(name, type, (void*)&node, &free_node)))
+ ABORT(r);
+
+ if (size < node->array.length)
+ ABORT(R_BAD_ARGS);
+
+ if (out != 0)
+ memcpy(out, node->array.data, node->array.length);
+ if (length != 0)
+ *length = node->array.length;
+
+ _status=0;
+ abort:
+ if (node && free_node) RFREE(node);
+ return(_status);
+}
+
+int
+nr_reg_set(char *name, int type, void *data)
+{
+ int r, _status;
+ nr_scalar_registry_node *node = 0;
+ int create_node = 0;
+ int changed = 0;
+ int free_node = 0;
+
+ if ((r=nr_reg_fetch_node(name, type, (void*)&node, &free_node)))
+ if (r == R_NOT_FOUND) {
+ create_node = 1;
+ free_node = 1;
+ }
+ else
+ ABORT(r);
+
+ if (create_node) {
+ if (!(node=(void*)RCALLOC(sizeof(nr_scalar_registry_node))))
+ ABORT(R_NO_MEMORY);
+
+ node->type = type;
+ }
+ else {
+ if (node->type != type)
+ ABORT(R_BAD_ARGS);
+ }
+
+ switch (type) {
+#define CASE(TYPE, _name, type) \
+ case TYPE: \
+ if (node->scalar._name != *(type*)data) { \
+ node->scalar._name = *(type*)data; \
+ if (! create_node) \
+ changed = 1; \
+ } \
+ break;
+ CASE(NR_REG_TYPE_CHAR, _char, char)
+ CASE(NR_REG_TYPE_UCHAR, _uchar, UCHAR)
+ CASE(NR_REG_TYPE_INT2, _nr_int2, INT2)
+ CASE(NR_REG_TYPE_UINT2, _nr_uint2, UINT2)
+ CASE(NR_REG_TYPE_INT4, _nr_int4, INT4)
+ CASE(NR_REG_TYPE_UINT4, _nr_uint4, UINT4)
+ CASE(NR_REG_TYPE_INT8, _nr_int8, INT8)
+ CASE(NR_REG_TYPE_UINT8, _nr_uint8, UINT8)
+ CASE(NR_REG_TYPE_DOUBLE, _double, double)
+#undef CASE
+
+ case NR_REG_TYPE_REGISTRY:
+ /* do nothing */
+ break;
+
+ default:
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ if (create_node) {
+ if ((r=nr_reg_insert_node(name, node)))
+ ABORT(r);
+ free_node = 0;
+ }
+ else {
+ if (changed) {
+ if ((r=nr_reg_change_node(name, node, node)))
+ ABORT(r);
+ free_node = 0;
+ }
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ if (node && free_node) RFREE(node);
+ }
+ return(_status);
+}
+
+int
+nr_reg_set_array(char *name, unsigned char type, UCHAR *data, size_t length)
+{
+ int r, _status;
+ nr_array_registry_node *old = 0;
+ nr_array_registry_node *node = 0;
+ int free_node = 0;
+ int added = 0;
+ int changed = 0;
+
+ if ((r=nr_reg_fetch_node(name, type, (void*)&old, &free_node))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ }
+ else {
+ assert(free_node == 0);
+ }
+
+ if (old) {
+ if (old->type != type)
+ ABORT(R_BAD_ARGS);
+
+ if (old->array.length != length
+ || memcmp(old->array.data, data, length)) {
+ changed = 1;
+
+ if (old->array.length < length) {
+ if (!(node=(void*)RCALLOC(sizeof(nr_array_registry_node)+length)))
+ ABORT(R_NO_MEMORY);
+ }
+ else {
+ node = old;
+ }
+ }
+ }
+ else {
+ if (!(node=(void*)RCALLOC(sizeof(nr_array_registry_node)+length)))
+ ABORT(R_NO_MEMORY);
+
+ added = 1;
+ }
+
+ if (added || changed) {
+ node->type = type;
+ node->array.length = length;
+ memcpy(node->array.data, data, length);
+ }
+
+ if (added) {
+ if ((r=nr_reg_insert_node(name, node)))
+ ABORT(r);
+ }
+ else if (changed) {
+ if ((r=nr_reg_change_node(name, node, old)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_set_parent_registries(char *name)
+{
+ int r, _status;
+ char *parent = 0;
+ char *dot;
+
+ if ((parent = r_strdup(name)) == 0)
+ ABORT(R_NO_MEMORY);
+
+ if ((dot = strrchr(parent, '.')) != 0) {
+ *dot = '\0';
+ if ((r=NR_reg_set_registry(parent)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ if (parent) RFREE(parent);
+ return(_status);
+}
+
+
+
+
+
+/* NON-STATIC METHODS */
+
+int
+nr_reg_is_valid(NR_registry name)
+{
+ int _status;
+ unsigned int length;
+ unsigned int i;
+
+ if (name == 0)
+ ABORT(R_BAD_ARGS);
+
+ /* make sure the key is null-terminated */
+ if (memchr(name, '\0', sizeof(NR_registry)) == 0)
+ ABORT(R_BAD_ARGS);
+
+ length = strlen(name);
+
+ /* cannot begin or end with a period */
+ if (name[0] == '.')
+ ABORT(R_BAD_ARGS);
+ if (strlen(name) > 0 && name[length-1] == '.')
+ ABORT(R_BAD_ARGS);
+
+ /* all characters cannot be space, and must be printable and not / */
+ for (i = 0; i < length; ++i) {
+ if (isspace(name[i]) || ! (isprint(name[i]) || name[i] == '/'))
+ ABORT(R_BAD_ARGS);
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "invalid name '%s'", name);
+ }
+ return(_status);
+}
+
+
+int
+nr_reg_compute_length(char *name, nr_registry_node *in, size_t *length)
+{
+ int _status;
+ nr_array_registry_node *node = (nr_array_registry_node*)in;
+
+ switch (node->type) {
+ case NR_REG_TYPE_STRING:
+ *length = node->array.length - 1;
+ break;
+ case NR_REG_TYPE_BYTES:
+ *length = node->array.length;
+ break;
+ case NR_REG_TYPE_CHAR:
+ *length = sizeof(char);
+ break;
+ case NR_REG_TYPE_UCHAR:
+ *length = sizeof(UCHAR);
+ break;
+ case NR_REG_TYPE_INT2:
+ case NR_REG_TYPE_UINT2:
+ *length = 2;
+ break;
+ case NR_REG_TYPE_INT4:
+ case NR_REG_TYPE_UINT4:
+ *length = 4;
+ break;
+ case NR_REG_TYPE_INT8:
+ case NR_REG_TYPE_UINT8:
+ *length = 8;
+ break;
+ case NR_REG_TYPE_DOUBLE:
+ *length = sizeof(double);
+ break;
+ case NR_REG_TYPE_REGISTRY:
+ *length = strlen(name);
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ break;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+
+/* VTBL METHODS */
+
+int
+nr_reg_local_init(nr_registry_module *me)
+{
+ int r, _status;
+
+ if (nr_registry == 0) {
+ if ((r=r_assoc_create(&nr_registry, r_assoc_crc32_hash_compute, 12)))
+ ABORT(r);
+
+ if ((r=nr_reg_cb_init()))
+ ABORT(r);
+
+ /* make sure NR_TOP_LEVEL_REGISTRY always exists */
+ if ((r=nr_reg_local_set_registry(NR_TOP_LEVEL_REGISTRY)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+#define NRREGGET(func, TYPE, type) \
+int \
+func(NR_registry name, type *out) \
+{ \
+ return nr_reg_get(name, TYPE, out); \
+}
+
+NRREGGET(nr_reg_local_get_char, NR_REG_TYPE_CHAR, char)
+NRREGGET(nr_reg_local_get_uchar, NR_REG_TYPE_UCHAR, UCHAR)
+NRREGGET(nr_reg_local_get_int2, NR_REG_TYPE_INT2, INT2)
+NRREGGET(nr_reg_local_get_uint2, NR_REG_TYPE_UINT2, UINT2)
+NRREGGET(nr_reg_local_get_int4, NR_REG_TYPE_INT4, INT4)
+NRREGGET(nr_reg_local_get_uint4, NR_REG_TYPE_UINT4, UINT4)
+NRREGGET(nr_reg_local_get_int8, NR_REG_TYPE_INT8, INT8)
+NRREGGET(nr_reg_local_get_uint8, NR_REG_TYPE_UINT8, UINT8)
+NRREGGET(nr_reg_local_get_double, NR_REG_TYPE_DOUBLE, double)
+
+int
+nr_reg_local_get_registry(NR_registry name, NR_registry out)
+{
+ int r, _status;
+ nr_scalar_registry_node *node = 0;
+ int free_node = 0;
+
+ if ((r=nr_reg_fetch_node(name, NR_REG_TYPE_REGISTRY, (void*)&node, &free_node)))
+ ABORT(r);
+
+ strncpy(out, name, sizeof(NR_registry));
+
+ _status=0;
+ abort:
+ if (free_node) RFREE(node);
+ return(_status);
+
+}
+
+int
+nr_reg_local_get_bytes(NR_registry name, UCHAR *out, size_t size, size_t *length)
+{
+ return nr_reg_get_array(name, NR_REG_TYPE_BYTES, out, size, length);
+}
+
+int
+nr_reg_local_get_string(NR_registry name, char *out, size_t size)
+{
+ return nr_reg_get_array(name, NR_REG_TYPE_STRING, (UCHAR*)out, size, 0);
+}
+
+int
+nr_reg_local_get_length(NR_registry name, size_t *length)
+{
+ int r, _status;
+ nr_registry_node *node = 0;
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ if ((r=r_assoc_fetch(nr_registry, name, strlen(name)+1, (void*)&node)))
+ ABORT(r);
+
+ if ((r=nr_reg_compute_length(name, node, length)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_local_get_type(NR_registry name, NR_registry_type type)
+{
+ int r, _status;
+ nr_registry_node *node = 0;
+ char *str;
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ if ((r=r_assoc_fetch(nr_registry, name, strlen(name)+1, (void*)&node)))
+ ABORT(r);
+
+ str = nr_reg_type_name(node->type);
+ if (! str)
+ ABORT(R_BAD_ARGS);
+
+ strncpy(type, str, sizeof(NR_registry_type));
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+
+#define NRREGSET(func, TYPE, type) \
+int \
+func(NR_registry name, type data) \
+{ \
+ return nr_reg_set(name, TYPE, &data); \
+}
+
+NRREGSET(nr_reg_local_set_char, NR_REG_TYPE_CHAR, char)
+NRREGSET(nr_reg_local_set_uchar, NR_REG_TYPE_UCHAR, UCHAR)
+NRREGSET(nr_reg_local_set_int2, NR_REG_TYPE_INT2, INT2)
+NRREGSET(nr_reg_local_set_uint2, NR_REG_TYPE_UINT2, UINT2)
+NRREGSET(nr_reg_local_set_int4, NR_REG_TYPE_INT4, INT4)
+NRREGSET(nr_reg_local_set_uint4, NR_REG_TYPE_UINT4, UINT4)
+NRREGSET(nr_reg_local_set_int8, NR_REG_TYPE_INT8, INT8)
+NRREGSET(nr_reg_local_set_uint8, NR_REG_TYPE_UINT8, UINT8)
+NRREGSET(nr_reg_local_set_double, NR_REG_TYPE_DOUBLE, double)
+
+int
+nr_reg_local_set_registry(NR_registry name)
+{
+ return nr_reg_set(name, NR_REG_TYPE_REGISTRY, 0);
+}
+
+int
+nr_reg_local_set_bytes(NR_registry name, unsigned char *data, size_t length)
+{
+ return nr_reg_set_array(name, NR_REG_TYPE_BYTES, data, length);
+}
+
+int
+nr_reg_local_set_string(NR_registry name, char *data)
+{
+ return nr_reg_set_array(name, NR_REG_TYPE_STRING, (UCHAR*)data, strlen(data)+1);
+}
+
+int
+nr_reg_local_del(NR_registry name)
+{
+ int r, _status;
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ /* delete from NR_registry */
+ if ((r=nr_reg_local_iter(name, nr_reg_local_iter_delete, 0)))
+ ABORT(r);
+
+ if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_DELETE)))
+ ABORT(r);
+
+ /* if deleting from the root, re-insert the root */
+ if (! strcasecmp(name, NR_TOP_LEVEL_REGISTRY)) {
+ if ((r=nr_reg_local_set_registry(NR_TOP_LEVEL_REGISTRY)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ r_log(NR_LOG_REGISTRY,
+ (_status ? LOG_INFO : LOG_INFO),
+ "delete of '%s' %s", name,
+ (_status ? "FAILED" : "succeeded"));
+ return(_status);
+}
+
+int
+nr_reg_local_get_child_count(char *parent, size_t *count)
+{
+ int r, _status;
+ nr_registry_node *ignore1;
+ int ignore2;
+
+
+ if ((r=nr_reg_is_valid(parent)))
+ ABORT(r);
+
+ /* test to see whether it is present */
+ if ((r=nr_reg_fetch_node(parent, NR_REG_TYPE_REGISTRY, &ignore1, &ignore2)))
+ ABORT(r);
+
+ /* sanity check that there isn't any memory to free */
+ assert(ignore2 == 0);
+
+ *count = 0;
+
+ if ((r=nr_reg_local_iter(parent, nr_reg_local_count_children, count)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_local_get_children(NR_registry parent, NR_registry *data, size_t size, size_t *length)
+{
+ int r, _status;
+ nr_reg_find_children_arg arg;
+
+ if ((r=nr_reg_is_valid(parent)))
+ ABORT(r);
+
+ arg.children = data;
+ arg.size = size;
+ arg.length = 0;
+
+ if ((r=nr_reg_local_iter(parent, nr_reg_local_find_children, (void*)&arg))) {
+ if (r == R_INTERRUPTED)
+ ABORT(R_BAD_ARGS);
+ else
+ ABORT(r);
+ }
+
+ assert(sizeof(*arg.children) == sizeof(NR_registry));
+ qsort(arg.children, arg.length, sizeof(*arg.children), (void*)strcasecmp);
+
+ *length = arg.length;
+
+ _status = 0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_local_fin(NR_registry name)
+{
+ int r, _status;
+
+ if ((r=nr_reg_raise_event(name, NR_REG_CB_ACTION_FINAL)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_local_dump(int sorted)
+{
+ int r, _status;
+
+ if ((r=nr_reg_local_iter(NR_TOP_LEVEL_REGISTRY, nr_reg_local_dump_print, 0)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+
+
+static nr_registry_module_vtbl nr_reg_local_vtbl = {
+ nr_reg_local_init,
+ nr_reg_local_get_char,
+ nr_reg_local_get_uchar,
+ nr_reg_local_get_int2,
+ nr_reg_local_get_uint2,
+ nr_reg_local_get_int4,
+ nr_reg_local_get_uint4,
+ nr_reg_local_get_int8,
+ nr_reg_local_get_uint8,
+ nr_reg_local_get_double,
+ nr_reg_local_get_registry,
+ nr_reg_local_get_bytes,
+ nr_reg_local_get_string,
+ nr_reg_local_get_length,
+ nr_reg_local_get_type,
+ nr_reg_local_set_char,
+ nr_reg_local_set_uchar,
+ nr_reg_local_set_int2,
+ nr_reg_local_set_uint2,
+ nr_reg_local_set_int4,
+ nr_reg_local_set_uint4,
+ nr_reg_local_set_int8,
+ nr_reg_local_set_uint8,
+ nr_reg_local_set_double,
+ nr_reg_local_set_registry,
+ nr_reg_local_set_bytes,
+ nr_reg_local_set_string,
+ nr_reg_local_del,
+ nr_reg_local_get_child_count,
+ nr_reg_local_get_children,
+ nr_reg_local_fin,
+ nr_reg_local_dump
+};
+
+static nr_registry_module nr_reg_local_module = { 0, &nr_reg_local_vtbl };
+
+void *NR_REG_MODE_LOCAL = &nr_reg_local_module;
+
+
diff --git a/media/mtransport/third_party/nrappkit/src/registry/registry_vtbl.h b/media/mtransport/third_party/nrappkit/src/registry/registry_vtbl.h
new file mode 100644
index 000000000..0f75a03b9
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/registry_vtbl.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * registry_vtbl.h
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registry_vtbl.h,v $
+ * $Revision: 1.2 $
+ * $Date: 2006/08/16 19:39:14 $
+ *
+ *
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#ifndef __REGISTRY_VTBL_H__
+#define __REGISTRY_VTBL_H__
+
+typedef struct nr_registry_module_ nr_registry_module;
+
+typedef struct nr_registry_module_vtbl_ {
+ int (*init)(nr_registry_module*);
+
+ int (*get_char)(NR_registry name, char *out);
+ int (*get_uchar)(NR_registry name, UCHAR *out);
+ int (*get_int2)(NR_registry name, INT2 *out);
+ int (*get_uint2)(NR_registry name, UINT2 *out);
+ int (*get_int4)(NR_registry name, INT4 *out);
+ int (*get_uint4)(NR_registry name, UINT4 *out);
+ int (*get_int8)(NR_registry name, INT8 *out);
+ int (*get_uint8)(NR_registry name, UINT8 *out);
+ int (*get_double)(NR_registry name, double *out);
+ int (*get_registry)(NR_registry name, NR_registry out);
+
+ int (*get_bytes)(NR_registry name, UCHAR *out, size_t size, size_t *length);
+ int (*get_string)(NR_registry name, char *out, size_t size);
+ int (*get_length)(NR_registry name, size_t *length);
+ int (*get_type)(NR_registry name, NR_registry_type type);
+
+ int (*set_char)(NR_registry name, char data);
+ int (*set_uchar)(NR_registry name, UCHAR data);
+ int (*set_int2)(NR_registry name, INT2 data);
+ int (*set_uint2)(NR_registry name, UINT2 data);
+ int (*set_int4)(NR_registry name, INT4 data);
+ int (*set_uint4)(NR_registry name, UINT4 data);
+ int (*set_int8)(NR_registry name, INT8 data);
+ int (*set_uint8)(NR_registry name, UINT8 data);
+ int (*set_double)(NR_registry name, double data);
+ int (*set_registry)(NR_registry name);
+
+ int (*set_bytes)(NR_registry name, UCHAR *data, size_t length);
+ int (*set_string)(NR_registry name, char *data);
+
+ int (*del)(NR_registry name);
+
+ int (*get_child_count)(NR_registry parent, size_t *count);
+ int (*get_children)(NR_registry parent, NR_registry *data, size_t size, size_t *length);
+
+ int (*fin)(NR_registry name);
+
+ int (*dump)(int sorted);
+} nr_registry_module_vtbl;
+
+struct nr_registry_module_ {
+ void *handle;
+ nr_registry_module_vtbl *vtbl;
+};
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/registry/registrycb.c b/media/mtransport/third_party/nrappkit/src/registry/registrycb.c
new file mode 100644
index 000000000..d5aaf915d
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/registry/registrycb.c
@@ -0,0 +1,440 @@
+/*
+ *
+ * registrycb.c
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/registry/registrycb.c,v $
+ * $Revision: 1.3 $
+ * $Date: 2007/06/26 22:37:51 $
+ *
+ * Callback-related functions
+ *
+ *
+ * Copyright (C) 2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+#include "registry.h"
+#include "registry_int.h"
+#include "r_assoc.h"
+#include "r_errors.h"
+#include "nr_common.h"
+#include "r_log.h"
+#include "r_macros.h"
+
+static char CB_ACTIONS[] = { NR_REG_CB_ACTION_ADD,
+ NR_REG_CB_ACTION_DELETE,
+ NR_REG_CB_ACTION_CHANGE,
+ NR_REG_CB_ACTION_FINAL };
+
+typedef struct nr_reg_cb_info_ {
+ char action;
+ void (*cb)(void *cb_arg, char action, NR_registry name);
+ void *cb_arg;
+ NR_registry name;
+} nr_reg_cb_info;
+
+/* callbacks that are registered, a mapping from names like "foo.bar.baz"
+ * to an r_assoc which contains possibly several nr_reg_cb_info*'s */
+static r_assoc *nr_registry_callbacks = 0;
+
+//static size_t SIZEOF_CB_ID = (sizeof(void (*)()) + 1);
+#define SIZEOF_CB_ID (sizeof(void (*)()) + 1)
+
+static int nr_reg_validate_action(char action);
+static int nr_reg_assoc_destroy(void *ptr);
+static int compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID]);
+static int nr_reg_info_free(void *ptr);
+static int nr_reg_raise_event_recurse(char *name, char *tmp, int action);
+static int nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg);
+static int nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name));
+
+int
+nr_reg_cb_init()
+{
+ int r, _status;
+
+ if (nr_registry_callbacks == 0) {
+ if ((r=r_assoc_create(&nr_registry_callbacks, r_assoc_crc32_hash_compute, 12)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "Couldn't init notifications: %s", nr_strerror(_status));
+ }
+ return(_status);
+}
+
+int
+nr_reg_validate_action(char action)
+{
+ int _status;
+ int i;
+
+ for (i = 0; i < sizeof(CB_ACTIONS); ++i) {
+ if (action == CB_ACTIONS[i])
+ return 0;
+ }
+ ABORT(R_BAD_ARGS);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+nr_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg)
+{
+ int r, _status;
+ r_assoc *assoc;
+ int create_assoc = 0;
+ nr_reg_cb_info *info;
+ int create_info = 0;
+ unsigned char cb_id[SIZEOF_CB_ID];
+
+ if (name == 0 || cb == 0)
+ ABORT(R_BAD_ARGS);
+
+ if (nr_registry_callbacks == 0)
+ ABORT(R_FAILED);
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ if ((r=nr_reg_validate_action(action)))
+ ABORT(r);
+
+ if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) {
+ if (r == R_NOT_FOUND)
+ create_assoc = 1;
+ else
+ ABORT(r);
+ }
+
+ if (create_assoc) {
+ if ((r=r_assoc_create(&assoc, r_assoc_crc32_hash_compute, 5)))
+ ABORT(r);
+
+ if ((r=r_assoc_insert(nr_registry_callbacks, name, strlen(name)+1, assoc, 0, nr_reg_assoc_destroy, R_ASSOC_NEW)))
+ ABORT(r);
+ }
+
+ if ((r=compute_cb_id(cb, action, cb_id)))
+ ABORT(r);
+
+ if ((r=r_assoc_fetch(assoc, (char*)cb_id, SIZEOF_CB_ID, (void*)&info))) {
+ if (r == R_NOT_FOUND)
+ create_info = 1;
+ else
+ ABORT(r);
+ }
+
+ if (create_info) {
+ if (!(info=(void*)RCALLOC(sizeof(*info))))
+ ABORT(R_NO_MEMORY);
+ }
+
+ strncpy(info->name, name, sizeof(info->name));
+ info->action = action;
+ info->cb = cb;
+ info->cb_arg = cb_arg;
+
+ if (create_info) {
+ if ((r=r_assoc_insert(assoc, (char*)cb_id, SIZEOF_CB_ID, info, 0, nr_reg_info_free, R_ASSOC_NEW)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "register callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded"));
+
+ if (_status) {
+ if (create_info && info) RFREE(info);
+ if (create_assoc && assoc) nr_reg_assoc_destroy(&assoc);
+ }
+ return(_status);
+}
+
+int
+nr_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name))
+{
+ int r, _status;
+ r_assoc *assoc;
+ int size;
+ unsigned char cb_id[SIZEOF_CB_ID];
+
+ if (name == 0 || cb == 0)
+ ABORT(R_BAD_ARGS);
+
+ if (nr_registry_callbacks == 0)
+ ABORT(R_FAILED);
+
+ if ((r=nr_reg_is_valid(name)))
+ ABORT(r);
+
+ if ((r=nr_reg_validate_action(action)))
+ ABORT(r);
+
+ if ((r=r_assoc_fetch(nr_registry_callbacks, name, strlen(name)+1, (void*)&assoc))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ }
+ else {
+ if ((r=compute_cb_id(cb, action, cb_id)))
+ ABORT(r);
+
+ if ((r=r_assoc_delete(assoc, (char*)cb_id, SIZEOF_CB_ID))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+ }
+
+ if ((r=r_assoc_num_elements(assoc, &size)))
+ ABORT(r);
+
+ if (size == 0) {
+ if ((r=r_assoc_delete(nr_registry_callbacks, name, strlen(name)+1)))
+ ABORT(r);
+ }
+ }
+
+ _status=0;
+ abort:
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "unregister callback %p on '%s' for '%s' %s", cb, name, nr_reg_action_name(action), (_status ? "FAILED" : "succeeded"));
+
+ return(_status);
+}
+
+int
+compute_cb_id(void *cb, char action, unsigned char cb_id[SIZEOF_CB_ID])
+{
+ /* callbacks are identified by the pointer to the cb function plus
+ * the action being watched */
+ assert(sizeof(cb) == sizeof(void (*)()));
+ assert(sizeof(cb) == (SIZEOF_CB_ID - 1));
+
+ memcpy(cb_id, &(cb), sizeof(cb));
+ cb_id[SIZEOF_CB_ID-1] = action;
+
+ return 0;
+}
+
+char *
+nr_reg_action_name(int action)
+{
+ char *name = "*Unknown*";
+
+ switch (action) {
+ case NR_REG_CB_ACTION_ADD: name = "add"; break;
+ case NR_REG_CB_ACTION_DELETE: name = "delete"; break;
+ case NR_REG_CB_ACTION_CHANGE: name = "change"; break;
+ case NR_REG_CB_ACTION_FINAL: name = "final"; break;
+ }
+
+ return name;
+}
+
+int
+nr_reg_assoc_destroy(void *ptr)
+{
+ return r_assoc_destroy((r_assoc**)&ptr);
+}
+
+int
+nr_reg_info_free(void *ptr)
+{
+ RFREE(ptr);
+ return 0;
+}
+
+/* call with tmp=0 */
+int
+nr_reg_raise_event_recurse(char *name, char *tmp, int action)
+{
+ int r, _status;
+ r_assoc *assoc;
+ nr_reg_cb_info *info;
+ r_assoc_iterator iter;
+ char *key;
+ int keyl;
+ char *c;
+ int free_tmp = 0;
+ int count;
+
+ if (tmp == 0) {
+ if (!(tmp = (char*)r_strdup(name)))
+ ABORT(R_NO_MEMORY);
+ free_tmp = 1;
+ }
+
+ if ((r=r_assoc_fetch(nr_registry_callbacks, tmp, strlen(tmp)+1, (void*)&assoc))) {
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found on '%s'", tmp);
+ }
+ else {
+ if (!r_assoc_num_elements(assoc, &count)) {
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "%d callback%s found on '%s'",
+ count, ((count == 1) ? "" : "s"), tmp);
+ }
+
+ if ((r=r_assoc_init_iter(assoc, &iter)))
+ ABORT(r);
+
+ for (;;) {
+ if ((r=r_assoc_iter(&iter, (void*)&key, &keyl, (void*)&info))) {
+ if (r == R_EOD)
+ break;
+ else
+ ABORT(r);
+ }
+
+ if (info->action == action) {
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG,
+ "Invoking callback %p for '%s'",
+ info->cb,
+ nr_reg_action_name(info->action));
+
+ (void)info->cb(info->cb_arg, action, name);
+ }
+ else {
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG,
+ "Skipping callback %p for '%s'",
+ info->cb,
+ nr_reg_action_name(info->action));
+ }
+ }
+ }
+
+ if (strlen(tmp) > 0) {
+ c = strrchr(tmp, '.');
+ if (c != 0)
+ *c = '\0';
+ else
+ tmp[0] = '\0';
+
+ if ((r=nr_reg_raise_event_recurse(name, tmp, action)))
+ ABORT(r);
+ }
+
+ _status=0;
+ abort:
+ if (free_tmp && tmp != 0) RFREE(tmp);
+ return(_status);
+}
+
+
+/* NON-STATIC METHODS */
+
+int
+nr_reg_raise_event(char *name, int action)
+{
+ int r, _status;
+ int count;
+ char *event = nr_reg_action_name(action);
+
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "raising event '%s' on '%s'", event, name);
+
+ if (name == 0)
+ ABORT(R_BAD_ARGS);
+
+ if ((r=nr_reg_validate_action(action)))
+ ABORT(r);
+
+ if ((r=r_assoc_num_elements(nr_registry_callbacks, &count)))
+ ABORT(r);
+
+ if (count > 0) {
+ if ((r=nr_reg_raise_event_recurse(name, 0, action)))
+ ABORT(r);
+ }
+ else {
+ r_log(NR_LOG_REGISTRY, LOG_DEBUG, "No callbacks found");
+ return 0;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+
+/* PUBLIC METHODS */
+
+int
+NR_reg_register_callback(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name), void *cb_arg)
+{
+ int r, _status;
+ int i;
+
+ for (i = 0; i < sizeof(CB_ACTIONS); ++i) {
+ if (action & CB_ACTIONS[i]) {
+ if ((r=nr_reg_register_callback(name, CB_ACTIONS[i], cb, cb_arg)))
+ ABORT(r);
+
+ action &= ~(CB_ACTIONS[i]);
+ }
+ }
+
+ if (action)
+ ABORT(R_BAD_ARGS);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+int
+NR_reg_unregister_callback(char *name, char action, void (*cb)(void *cb_arg, char action, NR_registry name))
+{
+ int r, _status;
+ int i;
+
+ for (i = 0; i < sizeof(CB_ACTIONS); ++i) {
+ if (action & CB_ACTIONS[i]) {
+ if ((r=nr_reg_unregister_callback(name, CB_ACTIONS[i], cb)))
+ ABORT(r);
+
+ action &= ~(CB_ACTIONS[i]);
+ }
+ }
+
+ if (action)
+ ABORT(R_BAD_ARGS);
+
+ _status=0;
+ abort:
+ return(_status);
+}
diff --git a/media/mtransport/third_party/nrappkit/src/share/nr_api.h b/media/mtransport/third_party/nrappkit/src/share/nr_api.h
new file mode 100644
index 000000000..ce6055f0d
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/share/nr_api.h
@@ -0,0 +1,51 @@
+/**
+ nr_pce.h
+
+
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@networkresonance.com Wed Jul 19 13:18:39 2006
+ */
+
+
+#ifndef _nr_pce_h
+#define _nr_pce_h
+
+#include <sys/queue.h>
+#include <csi_platform.h>
+#include <r_common.h>
+#include <r_log.h>
+#include <nrstats.h>
+#include <nr_plugin.h>
+#include <async_wait.h>
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/share/nr_common.h b/media/mtransport/third_party/nrappkit/src/share/nr_common.h
new file mode 100644
index 000000000..ac3ff4400
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/share/nr_common.h
@@ -0,0 +1,108 @@
+/**
+ nr_common.h
+
+
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+
+#ifndef _nr_common_h
+#define _nr_common_h
+
+#include <csi_platform.h>
+
+#ifdef USE_MPATROL
+#define USEDEBUG 1
+#include <mpatrol.h>
+#endif
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <errno.h>
+#else
+#include <sys/errno.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <r_log.h>
+
+extern int NR_LOG_REASSD;
+
+#include "registry.h"
+#include "nrstats.h"
+
+typedef struct nr_captured_packet_ {
+ UCHAR cap_interface; /* 1 for primary, 2 for secondary */
+ struct timeval ts; /* The time this packet was captured */
+ UINT4 len; /* The length of the packet */
+ UINT8 packet_number; /* The listener's packet index */
+} nr_captured_packet;
+
+#ifndef NR_ROOT_PATH
+#define NR_ROOT_PATH "/usr/local/ctc/"
+#endif
+
+#define NR_ARCHIVE_DIR NR_ROOT_PATH "archive/"
+#define NR_TEMP_DIR NR_ROOT_PATH "tmp/"
+#define NR_ARCHIVE_STATEFILE NR_ROOT_PATH "archive/state"
+#define NR_CAPTURED_PID_FILENAME NR_ROOT_PATH "captured.pid"
+#define NR_REASSD_PID_FILENAME NR_ROOT_PATH "reassd.pid"
+#define NR_MODE_FILENAME NR_ROOT_PATH "mode.txt"
+
+char *nr_revision_number(void);
+
+
+
+
+/* Memory buckets for CTC memory types */
+#define NR_MEM_TCP 1
+#define NR_MEM_HTTP 2
+#define NR_MEM_DELIVERY 3
+#define NR_MEM_OUT_HM 4
+#define NR_MEM_OUT_SSL 5
+#define NR_MEM_SSL 7
+#define NR_MEM_COMMON 8
+#define NR_MEM_CODEC 9
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/share/nr_reg_keys.h b/media/mtransport/third_party/nrappkit/src/share/nr_reg_keys.h
new file mode 100644
index 000000000..b051f51d4
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/share/nr_reg_keys.h
@@ -0,0 +1,167 @@
+/*
+ *
+ * nr_reg_keys.h
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/share/nr_reg_keys.h,v $
+ * $Revision: 1.3 $
+ * $Date: 2008/01/29 00:34:00 $
+ *
+ *
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#ifndef __NR_REG_KEYS_H__
+#define __NR_REG_KEYS_H__
+
+#include <stdio.h>
+
+#define NR_REG_NAME_LENGTH_MIN 1
+#define NR_REG_NAME_LENGTH_MAX 32
+
+#define NR_REG_HOSTNAME "hostname"
+#define NR_REG_ADDRESS "address"
+#define NR_REG_NETMASKSIZE "netmasksize"
+#define NR_REG_ADDRESS_NETMASKSIZE "address/netmasksize" /* use only in clic files */
+#define NR_REG_PORT "port"
+
+#define NR_REG_LOGGING_SYSLOG_ENABLED "logging.syslog.enabled"
+#define NR_REG_LOGGING_SYSLOG_SERVERS "logging.syslog.servers"
+#define NR_REG_LOGGING_SYSLOG_FACILITY "logging.syslog.facility"
+#define NR_REG_LOGGING_SYSLOG_LEVEL "logging.syslog.level"
+
+#define NR_REG_CAPTURED_DAEMONS "captured.daemons"
+
+#define NR_REG_LISTEND_ENABLED "listend.enabled"
+
+#define NR_REG_LISTEND_MAX_INPUT_BUFFER_SIZE "listend.max_input_buffer_size"
+#define NR_REG_LISTEND_MAX_INPUT_BUFFER_SIZE_MIN 1 // 1 byte?
+#define NR_REG_LISTEND_MAX_INPUT_BUFFER_SIZE_MAX (10ULL*1024*1024*1024) // 10 GB
+
+#define NR_REG_LISTEND_INTERFACE "listend.interface"
+#define NR_REG_LISTEND_INTERFACE_PRIMARY "listend.interface.primary"
+#define NR_REG_LISTEND_INTERFACE_SECONDARY "listend.interface.secondary"
+#define NR_REG_LISTEND_LISTEN_ON_BOTH_INTERFACES "listend.interface.listen_on_both_interfaces"
+#define NR_REG_LISTEND_ENABLE_VLAN "listend.enable_vlan"
+
+#define NR_REG_LISTEND_LISTEN_TO "listend.listen_to"
+#define NR_REG_LISTEND_IGNORE "listend.ignore"
+
+#define NR_REG_LISTEND_PORT_MIN 0
+#define NR_REG_LISTEND_PORT_MAX 65535
+
+#define NR_REG_REASSD_MAX_MEMORY_CONSUMPTION "reassd.max_memory_consumption"
+#define NR_REG_REASSD_MAX_MEMORY_CONSUMPTION_MIN (10*1024) // 100 KB
+#define NR_REG_REASSD_MAX_MEMORY_CONSUMPTION_MAX (10ULL*1024*1024*1024) // 10 GB
+
+#define NR_REG_REASSD_DECODER_TCP_IGNORE_CHECKSUMS "reassd.decoder.tcp.ignore_checksums"
+
+#define NR_REG_REASSD_DECODER_TCP_MAX_CONNECTIONS_IN_SYN_STATE "reassd.decoder.tcp.max_connections_in_syn_state"
+#define NR_REG_REASSD_DECODER_TCP_MAX_CONNECTIONS_IN_SYN_STATE_MIN 1
+#define NR_REG_REASSD_DECODER_TCP_MAX_CONNECTIONS_IN_SYN_STATE_MAX 500000
+
+#define NR_REG_REASSD_DECODER_TCP_MAX_SIMULTANEOUS_CONNECTIONS "reassd.decoder.tcp.max_simultaneous_connections"
+#define NR_REG_REASSD_DECODER_TCP_MAX_SIMULTANEOUS_CONNECTIONS_MIN 1
+#define NR_REG_REASSD_DECODER_TCP_MAX_SIMULTANEOUS_CONNECTIONS_MAX 1000000 // 1 million
+
+#define NR_REG_REASSD_DECODER_SSL_MAX_SESSION_CACHE_SIZE "reassd.decoder.ssl.max_session_cache_size"
+#define NR_REG_REASSD_DECODER_SSL_MAX_SESSION_CACHE_SIZE_MIN 0
+#define NR_REG_REASSD_DECODER_SSL_MAX_SESSION_CACHE_SIZE_MAX (1ULL*1024*1024*1024) // 1GB
+
+#define NR_REG_REASSD_DECODER_SSL_REVEAL_LOCAL_KEYS "reassd.decoder.ssl.reveal.local.keys"
+
+#define NR_REG_REASSD_DECODER_HTTP_HANGING_RESPONSE_TIMEOUT "reassd.decoder.http.hanging_response_timeout"
+#define NR_REG_REASSD_DECODER_HTTP_HANGING_RESPONSE_TIMEOUT_MIN 1
+#define NR_REG_REASSD_DECODER_HTTP_HANGING_RESPONSE_TIMEOUT_MAX 1023
+
+#define NR_REG_REASSD_DECODER_HTTP_HANGING_TRANSMISSION_TIMEOUT "reassd.decoder.http.hanging_transmission_timeout"
+#define NR_REG_REASSD_DECODER_HTTP_HANGING_TRANSMISSION_TIMEOUT_MIN 1
+#define NR_REG_REASSD_DECODER_HTTP_HANGING_TRANSMISSION_TIMEOUT_MAX 1023
+
+#define NR_REG_REASSD_DECODER_HTTP_MAX_HTTP_MESSAGE_SIZE "reassd.decoder.http.max_http_message_size"
+#define NR_REG_REASSD_DECODER_HTTP_MAX_HTTP_MESSAGE_SIZE_MIN 0
+#define NR_REG_REASSD_DECODER_HTTP_MAX_HTTP_MESSAGE_SIZE_MAX (10ULL*1024*1024*1024)
+
+/* PCE-only: */
+#define NR_REG_LISTEND_ARCHIIVE "listend.archive"
+#define NR_REG_LISTEND_ARCHIVE_MAX_SIZE "listend.archive.max_size"
+#define NR_REG_LISTEND_ARCHIVE_MAX_SIZE_MIN 0
+#define NR_REG_LISTEND_ARCHIVE_MAX_SIZE_MAX (10ULL*1024*1024*1024*1024) // 10 TB
+
+#define NR_REG_LISTEND_ARCHIVE_RECORDING_ENABLED "listend.archive.recording_enabled"
+
+#define NR_REG_REASSD_DECODER_NET_DELIVER_BATCH_INTERVAL "reassd.decoder.net_deliver.batch_interval"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_BATCH_INTERVAL_MIN 0
+#define NR_REG_REASSD_DECODER_NET_DELIVER_BATCH_INTERVAL_MAX 1023
+
+#define NR_REG_REASSD_DECODER_NET_DELIVER_MAX_QUEUE_DEPTH "reassd.decoder.net_deliver.max_queue_depth"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_MAX_QUEUE_DEPTH_MIN 0
+#define NR_REG_REASSD_DECODER_NET_DELIVER_MAX_QUEUE_DEPTH_MAX (1ULL*1024*1024*1024) // 1 GB
+
+#define NR_REG_REASSD_DECODER_NET_DELIVER_MY_KEY_CERTIFICATE "reassd.decoder.net_deliver.my_key.certificate"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_MY_KEY_PRIVATE_KEY "reassd.decoder.net_deliver.my_key.private_key"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_PEER "reassd.decoder.net_deliver.peer"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_PEER_PORT_MIN 0
+#define NR_REG_REASSD_DECODER_NET_DELIVER_PEER_PORT_MAX 65535
+
+#define NR_REG_REASSD_DECODER_NET_DELIVER_POLLING_INTERVAL "reassd.decoder.net_deliver.polling_interval"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_POLLING_INTERVAL_MIN 1
+#define NR_REG_REASSD_DECODER_NET_DELIVER_POLLING_INTERVAL_MAX 1023
+
+#define NR_REG_REASSD_DECODER_NET_DELIVER_STATELESS "reassd.decoder.net_deliver.stateless"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_WATCHDOG_TIMER "reassd.decoder.net_deliver.watchdog_timer"
+#define NR_REG_REASSD_DECODER_NET_DELIVER_WATCHDOG_TIMER_MIN 0
+#define NR_REG_REASSD_DECODER_NET_DELIVER_WATCHDOG_TIMER_MAX 666
+
+/* ASA-only: */
+#define NR_REG_MIGRATE_ENABLED "migrate.enabled"
+
+#define NR_REG_MIGRATE_INACTIVITY_TIMEOUT "migrate.inactivity_timeout"
+#define NR_REG_MIGRATE_INACTIVITY_TIMEOUT_MIN 0
+#define NR_REG_MIGRATE_INACTIVITY_TIMEOUT_MAX 1023
+
+#define NR_REG_MIGRATE_MIN_LOCAL_SIZE "migrate.min_local_size"
+#define NR_REG_MIGRATE_MIN_OVERLAP_SIZE "migrate.min_overlap_size"
+#define NR_REG_MIGRATE_RETRANSMIT_FREQUENCY "migrate.retransmit_frequency"
+#define NR_REG_MIGRATE_RETRIES "migrate.retries"
+#define NR_MIGRATE_LOCATION_NUMBER_MIN 0
+#define NR_MIGRATE_LOCATION_NUMBER_MAX 255
+
+#define NR_REG_REVELATION_ENABLE "revelation.enabled"
+#define NR_REG_REVELATION_MAX_PER_HOUR "revelation.max_per_hour"
+#define NR_REG_REVELATION_MAX_PER_HOUR_PER_PORTAL "revelation.max_per_hour_per_portal"
+
+/* Appliance-only: */
+#define NR_REG_SNMP_ENABLED "snmp.enabled"
+#define NR_REG_CLOCK_TIMEZONE "clock.timezone"
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/stats/nrstats.h b/media/mtransport/third_party/nrappkit/src/stats/nrstats.h
new file mode 100644
index 000000000..0b6c2bc3c
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/stats/nrstats.h
@@ -0,0 +1,118 @@
+/*
+ *
+ * nrstats.h
+ *
+ * $Source: /Users/ekr/tmp/nrappkit-dump/nrappkit/src/stats/nrstats.h,v $
+ * $Revision: 1.4 $
+ * $Date: 2007/06/26 22:37:55 $
+ *
+ * API for keeping and sharing statistics
+ *
+ *
+ * Copyright (C) 2003-2005, Network Resonance, Inc.
+ * Copyright (C) 2006, Network Resonance, Inc.
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Network Resonance, Inc. nor the name of any
+ * contributors to this software may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+#ifndef __NRSTATS_H__
+#define __NRSTATS_H__
+
+#include <sys/types.h>
+#ifdef WIN32
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+#include <r_types.h>
+
+#ifndef CAPTURE_USER
+#define CAPTURE_USER "pcecap"
+#endif
+
+#define NR_MAX_STATS_TYPES 256 /* max number of stats objects */
+#define NR_MAX_STATS_TYPE_NAME 26
+
+typedef struct NR_stats_type_ {
+ char name[NR_MAX_STATS_TYPE_NAME];
+ int (*reset)(void *stats);
+ int (*print)(void *stats, char *stat_namespace, void (*output)(void *handle, const char *fmt, ...), void *handle);
+ int (*get_lib_name)(char **libname);
+ unsigned int size;
+} NR_stats_type;
+
+typedef struct NR_stats_app_ {
+ time_t last_counter_reset;
+ time_t last_restart;
+ UINT8 total_restarts;
+ char version[64];
+} NR_stats_app;
+
+extern NR_stats_type *NR_stats_type_app;
+
+/* everything measured in bytes */
+typedef struct NR_stats_memory_ {
+ UINT8 current_size;
+ UINT8 max_size;
+ UINT8 in_use;
+ UINT8 in_use_max;
+} NR_stats_memory;
+
+extern NR_stats_type *NR_stats_type_memory;
+
+/* all functions below return 0 on success, else they return an
+ * error code */
+
+/* if errprintf is null, warnings and errors will be sent to /dev/null */
+extern int NR_stats_startup(char *app_name, char *user_name, void (*errprintf)(void *handle, const char *fmt, ...), void *errhandle);
+extern int NR_stats_shutdown(void);
+#define NR_STATS_CREATE (1<<0)
+extern int NR_stats_get(char *module_name, NR_stats_type *type, int flag, void **stats);
+extern int NR_stats_clear(void *stats); /* zeroizes */
+extern int NR_stats_reset(void *stats); /* zeros "speedometers" */
+extern int NR_stats_register(NR_stats_type *type);
+extern int NR_stats_acquire_mutex(void *stats);
+extern int NR_stats_release_mutex(void *stats);
+// TODO: should _get_names take an app_name argument (0==all)????
+extern int NR_stats_get_names(unsigned int *nnames, char ***names);
+extern int NR_stats_get_by_name(char *name, NR_stats_type **type, void **stats);
+extern int NR_stats_get_lib_name(void *stats, char **lib_name);
+extern int NR_stats_rmids(void);
+
+extern char *NR_prefix_to_stats_module(char *prefix);
+
+#define NR_INCREMENT_STAT(stat) do { \
+ stat++; if(stat>stat##_max) stat##_max=stat; \
+ } while (0)
+#define NR_UPDATE_STAT(stat,newval) do { \
+ stat=newval; if(stat>stat##_max) stat##_max=stat; \
+ } while (0)
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/util/byteorder.c b/media/mtransport/third_party/nrappkit/src/util/byteorder.c
new file mode 100644
index 000000000..b62e72b8a
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/byteorder.c
@@ -0,0 +1,76 @@
+/**
+ byteorder.c
+
+
+ Copyright (C) 2007, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ briank@networkresonance.com Wed May 16 16:46:00 PDT 2007
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: byteorder.c,v 1.2 2007/06/26 22:37:55 adamcain Exp $";
+
+#include "nr_common.h"
+#ifndef WIN32
+#include <arpa/inet.h>
+#endif
+#include "r_types.h"
+#include "byteorder.h"
+
+#define IS_BIG_ENDIAN (htonl(0x1) == 0x1)
+
+#define BYTE(n,i) (((UCHAR*)&(n))[(i)])
+#define SWAP(n,x,y) tmp=BYTE((n),(x)), \
+ BYTE((n),(x))=BYTE((n),(y)), \
+ BYTE((n),(y))=tmp
+
+UINT8
+nr_htonll(UINT8 hostlonglong)
+{
+ UINT8 netlonglong = hostlonglong;
+ UCHAR tmp;
+
+ if (!IS_BIG_ENDIAN) {
+ SWAP(netlonglong, 0, 7);
+ SWAP(netlonglong, 1, 6);
+ SWAP(netlonglong, 2, 5);
+ SWAP(netlonglong, 3, 4);
+ }
+
+ return netlonglong;
+}
+
+UINT8
+nr_ntohll(UINT8 netlonglong)
+{
+ return nr_htonll(netlonglong);
+}
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/byteorder.h b/media/mtransport/third_party/nrappkit/src/util/byteorder.h
new file mode 100644
index 000000000..8df0589a6
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/byteorder.h
@@ -0,0 +1,47 @@
+/**
+ byteorder.h
+
+
+ Copyright (C) 2007, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ briank@networkresonance.com Wed May 16 16:46:00 PDT 2007
+ */
+
+
+#ifndef _byteorder_h
+#define _byteorder_h
+
+UINT8 nr_htonll(UINT8 hostlonglong);
+
+UINT8 nr_ntohll(UINT8 netlonglong);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/hex.c b/media/mtransport/third_party/nrappkit/src/util/hex.c
new file mode 100644
index 000000000..6ae6600f3
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/hex.c
@@ -0,0 +1,110 @@
+/**
+ hex.c
+
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ briank@network-resonance.com
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: hex.c,v 1.3 2007/06/26 22:37:56 adamcain Exp $";
+
+#include <sys/types.h>
+#include <string.h>
+#include <ctype.h>
+#include "r_common.h"
+#include "hex.h"
+#include "r_log.h"
+
+static char bin2hex_map[][3] = { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff" };
+
+static int hex2bin_map[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0 /* '0' */, 1 /* '1' */, 2 /* '2' */, 3 /* '3' */, 4 /* '4' */, 5 /* '5' */, 6 /* '6' */, 7 /* '7' */, 8 /* '8' */, 9 /* '9' */, -1, -1, -1, -1, -1, -1, -1, 10 /* 'A' */, 11 /* 'B' */, 12 /* 'C' */, 13 /* 'D' */, 14 /* 'E' */, 15 /* 'F' */, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10 /* 'a' */, 11 /* 'b' */, 12 /* 'c' */, 13 /* 'd' */, 14 /* 'e' */, 15 /* 'f' */, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+int
+nr_nbin2hex(UCHAR *bin, int binlen, char hex[], size_t size, int *len)
+{
+ int _status;
+ int i;
+
+ if (size < (2*binlen))
+ ABORT(R_BAD_ARGS);
+
+ for (i = 0; i < binlen; ++i) {
+ *hex++ = bin2hex_map[bin[i]][0];
+ *hex++ = bin2hex_map[bin[i]][1];
+ }
+
+ if (size >= (2*binlen)+1)
+ *hex = '\0';
+
+ *len = (2*binlen);
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+
+int
+nr_nhex2bin(char *hex, int hexlen, UCHAR bin[], size_t size, int *len)
+{
+ int _status;
+ int binlen;
+ int h1;
+ int h2;
+ int i;
+
+ if (hexlen % 2)
+ ABORT(R_BAD_ARGS);
+
+ binlen = hexlen/2;
+
+ if (size < binlen)
+ ABORT(R_BAD_ARGS);
+
+ for (i = 0; i < binlen; ++i) {
+ h1 = hex2bin_map[(int)*hex++];
+ h2 = hex2bin_map[(int)*hex++];
+
+ if (h1 == -1 || h2 == -1)
+ ABORT(R_BAD_ARGS);
+
+ bin[i] = (h1 << 4) | h2;
+ }
+
+ *len = binlen;
+
+ _status=0;
+ abort:
+ return(_status);
+}
diff --git a/media/mtransport/third_party/nrappkit/src/util/hex.h b/media/mtransport/third_party/nrappkit/src/util/hex.h
new file mode 100644
index 000000000..be63594d1
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/hex.h
@@ -0,0 +1,47 @@
+/**
+ hex.h
+
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ briank@network-resonance.com
+ */
+
+
+#ifndef _hex_h
+#define _hex_h
+
+int nr_nbin2hex(UCHAR *bin, int binlen, char hex[], size_t size, int *len);
+int nr_nhex2bin(char *hex, int hexlen, UCHAR bin[], size_t size, int *len);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/assoc.h b/media/mtransport/third_party/nrappkit/src/util/libekr/assoc.h
new file mode 100644
index 000000000..013e78868
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/assoc.h
@@ -0,0 +1,90 @@
+/**
+ assoc.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ assoc.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: assoc.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Sun Jan 17 17:56:35 1999
+ */
+
+
+#ifndef _assoc_h
+#define _assoc_h
+
+typedef struct assoc_ assoc;
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/debug.c b/media/mtransport/third_party/nrappkit/src/util/libekr/debug.c
new file mode 100644
index 000000000..6f913ac67
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/debug.c
@@ -0,0 +1,131 @@
+/**
+ debug.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ debug.c
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: debug.c,v 1.3 2007/06/26 22:37:57 adamcain Exp $
+
+
+ ekr@rtfm.com Wed Jan 6 17:08:58 1999
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: debug.c,v 1.3 2007/06/26 22:37:57 adamcain Exp $";
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "r_common.h"
+#include "debug.h"
+
+int nr_debug(int class,char *format,...)
+ {
+ va_list ap;
+
+ va_start(ap,format);
+#ifdef WIN32
+ vprintf(format,ap);
+ printf("\n");
+#else
+ vfprintf(stderr,format,ap);
+ fprintf(stderr,"\n");
+#endif
+ return(0);
+ }
+
+int nr_xdump(name,data,len)
+ char *name;
+ UCHAR *data;
+ int len;
+ {
+ int i;
+
+ if(name){
+ printf("%s[%d]=\n",name,len);
+ }
+ for(i=0;i<len;i++){
+
+ if((len>8) && i && !(i%12)){
+ printf("\n");
+ }
+ printf("%.2x ",data[i]&255);
+ }
+ if(i%12)
+ printf("\n");
+ return(0);
+ }
+
+
+
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/debug.h b/media/mtransport/third_party/nrappkit/src/util/libekr/debug.h
new file mode 100644
index 000000000..34f7b2fb5
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/debug.h
@@ -0,0 +1,94 @@
+/**
+ debug.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ debug.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: debug.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Wed Jan 6 17:13:00 1999
+ */
+
+
+#ifndef _debug_h
+#define _debug_h
+
+// Remove debugging mess from ssldump
+// #define DBG(a)
+
+int nr_debug(int class,char *format,...);
+int nr_xdump(char *name,UCHAR *data, int len);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.c
new file mode 100644
index 000000000..c83d8369f
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.c
@@ -0,0 +1,541 @@
+/**
+ r_assoc.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_assoc.c
+
+ This is an associative array implementation, using an open-chained
+ hash bucket technique.
+
+ Note that this implementation permits each data entry to have
+ separate copy constructors and destructors. This currently wastes
+ space, but could be implemented while saving space by using
+ the high order bit of the length value or somesuch.
+
+ The major problem with this code is it's not resizable, though it
+ could be made so.
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_assoc.c,v 1.4 2007/06/08 17:41:49 adamcain Exp $
+
+ ekr@rtfm.com Sun Jan 17 17:57:15 1999
+ */
+
+static char *RCSSTRING __UNUSED__ ="$Id: r_assoc.c,v 1.4 2007/06/08 17:41:49 adamcain Exp $";
+
+#include <r_common.h>
+#include <string.h>
+#include "r_assoc.h"
+
+typedef struct r_assoc_el_ {
+ char *key;
+ int key_len;
+ void *data;
+ struct r_assoc_el_ *prev;
+ struct r_assoc_el_ *next;
+ int (*copy)(void **n,void *old);
+ int (*destroy)(void *ptr);
+} r_assoc_el;
+
+struct r_assoc_ {
+ int size;
+ int bits;
+ int (*hash_func)(char *key,int len,int size);
+ r_assoc_el **chains;
+ UINT4 num_elements;
+};
+
+#define DEFAULT_TABLE_BITS 5
+
+static int destroy_assoc_chain(r_assoc_el *chain);
+static int r_assoc_fetch_bucket(r_assoc *assoc,
+ char *key,int len,r_assoc_el **bucketp);
+static int copy_assoc_chain(r_assoc_el **knewp, r_assoc_el *old);
+
+int r_assoc_create(assocp,hash_func,bits)
+ r_assoc **assocp;
+ int (*hash_func)(char *key,int len,int size);
+ int bits;
+ {
+ r_assoc *assoc=0;
+ int _status;
+
+ if(!(assoc=(r_assoc *)RCALLOC(sizeof(r_assoc))))
+ ABORT(R_NO_MEMORY);
+ assoc->size=(1<<bits);
+ assoc->bits=bits;
+ assoc->hash_func=hash_func;
+
+ if(!(assoc->chains=(r_assoc_el **)RCALLOC(sizeof(r_assoc_el *)*
+ assoc->size)))
+ ABORT(R_NO_MEMORY);
+
+ *assocp=assoc;
+
+ _status=0;
+ abort:
+ if(_status){
+ r_assoc_destroy(&assoc);
+ }
+ return(_status);
+ }
+
+int r_assoc_destroy(assocp)
+ r_assoc **assocp;
+ {
+ r_assoc *assoc;
+ int i;
+
+ if(!assocp || !*assocp)
+ return(0);
+
+ assoc=*assocp;
+ for(i=0;i<assoc->size;i++)
+ destroy_assoc_chain(assoc->chains[i]);
+
+ RFREE(assoc->chains);
+ RFREE(*assocp);
+
+ return(0);
+ }
+
+static int destroy_assoc_chain(chain)
+ r_assoc_el *chain;
+ {
+ r_assoc_el *nxt;
+
+ while(chain){
+ nxt=chain->next;
+
+ if(chain->destroy)
+ chain->destroy(chain->data);
+
+ RFREE(chain->key);
+
+ RFREE(chain);
+ chain=nxt;
+ }
+
+ return(0);
+ }
+
+static int copy_assoc_chain(knewp,old)
+ r_assoc_el **knewp;
+ r_assoc_el *old;
+ {
+ r_assoc_el *knew=0,*ptr,*tmp;
+ int r,_status;
+
+ ptr=0; /* Pacify GCC's uninitialized warning.
+ It's not correct */
+ if(!old) {
+ *knewp=0;
+ return(0);
+ }
+ for(;old;old=old->next){
+ if(!(tmp=(r_assoc_el *)RCALLOC(sizeof(r_assoc_el))))
+ ABORT(R_NO_MEMORY);
+
+ if(!knew){
+ knew=tmp;
+ ptr=knew;
+ }
+ else{
+ ptr->next=tmp;
+ tmp->prev=ptr;
+ ptr=tmp;
+ }
+
+ ptr->destroy=old->destroy;
+ ptr->copy=old->copy;
+
+ if(old->copy){
+ if(r=old->copy(&ptr->data,old->data))
+ ABORT(r);
+ }
+ else
+ ptr->data=old->data;
+
+ if(!(ptr->key=(char *)RMALLOC(old->key_len)))
+ ABORT(R_NO_MEMORY);
+ memcpy(ptr->key,old->key,ptr->key_len=old->key_len);
+ }
+
+ *knewp=knew;
+
+ _status=0;
+ abort:
+ if(_status){
+ destroy_assoc_chain(knew);
+ }
+ return(_status);
+ }
+
+static int r_assoc_fetch_bucket(assoc,key,len,bucketp)
+ r_assoc *assoc;
+ char *key;
+ int len;
+ r_assoc_el **bucketp;
+ {
+ UINT4 hash_value;
+ r_assoc_el *bucket;
+
+ hash_value=assoc->hash_func(key,len,assoc->bits);
+
+ for(bucket=assoc->chains[hash_value];bucket;bucket=bucket->next){
+ if(bucket->key_len == len && !memcmp(bucket->key,key,len)){
+ *bucketp=bucket;
+ return(0);
+ }
+ }
+
+ return(R_NOT_FOUND);
+ }
+
+int r_assoc_fetch(assoc,key,len,datap)
+ r_assoc *assoc;
+ char *key;
+ int len;
+ void **datap;
+ {
+ r_assoc_el *bucket;
+ int r;
+
+ if(r=r_assoc_fetch_bucket(assoc,key,len,&bucket)){
+ if(r!=R_NOT_FOUND)
+ ERETURN(r);
+ return(r);
+ }
+
+ *datap=bucket->data;
+ return(0);
+ }
+
+int r_assoc_insert(assoc,key,len,data,copy,destroy,how)
+ r_assoc *assoc;
+ char *key;
+ int len;
+ void *data;
+ int (*copy)(void **knew,void *old);
+ int (*destroy)(void *ptr);
+ int how;
+ {
+ r_assoc_el *bucket,*new_bucket=0;
+ int r,_status;
+
+ if(r=r_assoc_fetch_bucket(assoc,key,len,&bucket)){
+ /*Note that we compute the hash value twice*/
+ UINT4 hash_value;
+
+ if(r!=R_NOT_FOUND)
+ ABORT(r);
+ hash_value=assoc->hash_func(key,len,assoc->bits);
+
+ if(!(new_bucket=(r_assoc_el *)RCALLOC(sizeof(r_assoc_el))))
+ ABORT(R_NO_MEMORY);
+ if(!(new_bucket->key=(char *)RMALLOC(len)))
+ ABORT(R_NO_MEMORY);
+ memcpy(new_bucket->key,key,len);
+ new_bucket->key_len=len;
+
+ /*Insert at the list head. Is FIFO a good algorithm?*/
+ if(assoc->chains[hash_value])
+ assoc->chains[hash_value]->prev=new_bucket;
+ new_bucket->next=assoc->chains[hash_value];
+ assoc->chains[hash_value]=new_bucket;
+ bucket=new_bucket;
+ }
+ else{
+ if(!(how&R_ASSOC_REPLACE))
+ ABORT(R_ALREADY);
+
+ if(bucket->destroy)
+ bucket->destroy(bucket->data);
+ }
+
+ bucket->data=data;
+ bucket->copy=copy;
+ bucket->destroy=destroy;
+ assoc->num_elements++;
+
+ _status=0;
+ abort:
+ if(_status && new_bucket){
+ RFREE(new_bucket->key);
+ RFREE(new_bucket);
+ }
+ return(_status);
+ }
+
+int r_assoc_delete(assoc,key,len)
+ r_assoc *assoc;
+ char *key;
+ int len;
+ {
+ int r;
+ r_assoc_el *bucket;
+ UINT4 hash_value;
+
+ if(r=r_assoc_fetch_bucket(assoc,key,len,&bucket)){
+ if(r!=R_NOT_FOUND)
+ ERETURN(r);
+ return(r);
+ }
+
+ /* Now remove the element from the hash chain */
+ if(bucket->prev){
+ bucket->prev->next=bucket->next;
+ }
+ else {
+ hash_value=assoc->hash_func(key,len,assoc->bits);
+ assoc->chains[hash_value]=bucket->next;
+ }
+
+ if(bucket->next)
+ bucket->next->prev=bucket->prev;
+
+ /* Remove the data */
+ if(bucket->destroy)
+ bucket->destroy(bucket->data);
+
+ RFREE(bucket->key);
+ RFREE(bucket);
+ assoc->num_elements--;
+
+ return(0);
+ }
+
+int r_assoc_copy(knewp,old)
+ r_assoc **knewp;
+ r_assoc *old;
+ {
+ int r,_status,i;
+ r_assoc *knew;
+
+ if(!(knew=(r_assoc *)RCALLOC(sizeof(r_assoc))))
+ ABORT(R_NO_MEMORY);
+ knew->size=old->size;
+ knew->bits=old->bits;
+ knew->hash_func=old->hash_func;
+
+ if(!(knew->chains=(r_assoc_el **)RCALLOC(sizeof(r_assoc_el)*old->size)))
+ ABORT(R_NO_MEMORY);
+ for(i=0;i<knew->size;i++){
+ if(r=copy_assoc_chain(knew->chains+i,old->chains[i]))
+ ABORT(r);
+ }
+ knew->num_elements=old->num_elements;
+
+ *knewp=knew;
+
+ _status=0;
+ abort:
+ if(_status){
+ r_assoc_destroy(&knew);
+ }
+ return(_status);
+ }
+
+int r_assoc_num_elements(r_assoc *assoc,int *sizep)
+ {
+ *sizep=assoc->num_elements;
+
+ return(0);
+ }
+
+int r_assoc_init_iter(assoc,iter)
+ r_assoc *assoc;
+ r_assoc_iterator *iter;
+ {
+ int i;
+
+ iter->assoc=assoc;
+ iter->prev_chain=-1;
+ iter->prev=0;
+
+ iter->next_chain=assoc->size;
+ iter->next=0;
+
+ for(i=0;i<assoc->size;i++){
+ if(assoc->chains[i]!=0){
+ iter->next_chain=i;
+ iter->next=assoc->chains[i];
+ break;
+ }
+ }
+
+ return(0);
+ }
+
+int r_assoc_iter(iter,key,keyl,val)
+ r_assoc_iterator *iter;
+ void **key;
+ int *keyl;
+ void **val;
+ {
+ int i;
+ r_assoc_el *ret;
+
+ if(!iter->next)
+ return(R_EOD);
+ ret=iter->next;
+
+ *key=ret->key;
+ *keyl=ret->key_len;
+ *val=ret->data;
+
+ /* Now increment */
+ iter->prev_chain=iter->next_chain;
+ iter->prev=iter->next;
+
+ /* More on this chain */
+ if(iter->next->next){
+ iter->next=iter->next->next;
+ }
+ else{
+ iter->next=0;
+
+ /* FInd the next occupied chain*/
+ for(i=iter->next_chain+1;i<iter->assoc->size;i++){
+ if(iter->assoc->chains[i]){
+ iter->next_chain=i;
+ iter->next=iter->assoc->chains[i];
+ break;
+ }
+ }
+ }
+
+ return(0);
+ }
+
+/* Delete the last returned value*/
+int r_assoc_iter_delete(iter)
+ r_assoc_iterator *iter;
+ {
+ /* First unhook it from the list*/
+ if(!iter->prev->prev){
+ /* First element*/
+ iter->assoc->chains[iter->prev_chain]=iter->prev->next;
+ }
+ else{
+ iter->prev->prev->next=iter->prev->next;
+ }
+
+ if(iter->prev->next){
+ iter->prev->next->prev=iter->prev->prev;
+ }
+
+ if (iter->prev->destroy)
+ iter->prev->destroy(iter->prev->data);
+
+ iter->assoc->num_elements--;
+ RFREE(iter->prev->key);
+ RFREE(iter->prev);
+ return(0);
+ }
+
+
+/*This is a hack from AMS. Supposedly, it's pretty good for strings, even
+ though it doesn't take into account all the data*/
+int r_assoc_simple_hash_compute(key,len,bits)
+ char *key;
+ int len;
+ int bits;
+ {
+ UINT4 h=0;
+
+ h=key[0] +(key[len-1] * len);
+
+ h &= (1<<bits) - 1;
+
+ return(h);
+ }
+
+
+int r_crc32(char *data,int len,UINT4 *crcval);
+
+int r_assoc_crc32_hash_compute(data,len,bits)
+ char *data;
+ int len;
+ int bits;
+ {
+ UINT4 res;
+ UINT4 mask;
+
+ /* First compute the CRC value */
+ if(r_crc32(data,len,&res))
+ ERETURN(R_INTERNAL);
+
+ mask=~(0xffffffff<<bits);
+
+ return(res & mask);
+ }
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.h
new file mode 100644
index 000000000..5311542c9
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_assoc.h
@@ -0,0 +1,126 @@
+/**
+ r_assoc.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_assoc.h
+
+ Associative array code. This code has the advantage that different
+ elements can have different create and destroy operators. Unfortunately,
+ this can waste space.
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_assoc.h,v 1.3 2007/06/08 22:16:08 adamcain Exp $
+
+
+ ekr@rtfm.com Sun Jan 17 17:57:18 1999
+ */
+
+
+#ifndef _r_assoc_h
+#define _r_assoc_h
+
+typedef struct r_assoc_ r_assoc;
+
+int r_assoc_create(r_assoc **assocp,
+ int (*hash_func)(char *,int,int),
+ int bits);
+int r_assoc_insert(r_assoc *assoc,char *key,int len,
+ void *value,int (*copy)(void **knew,void *old),
+ int (*destroy)(void *ptr),int how);
+#define R_ASSOC_REPLACE 0x1
+#define R_ASSOC_NEW 0x2
+
+int r_assoc_fetch(r_assoc *assoc,char *key, int len, void **value);
+int r_assoc_delete(r_assoc *assoc,char *key, int len);
+
+int r_assoc_copy(r_assoc **knew,r_assoc *old);
+int r_assoc_destroy(r_assoc **assocp);
+int r_assoc_simple_hash_compute(char *key, int len,int bits);
+int r_assoc_crc32_hash_compute(char *key, int len,int bits);
+
+/*We need iterators, but I haven't written them yet*/
+typedef struct r_assoc_iterator_ {
+ r_assoc *assoc;
+ int prev_chain;
+ struct r_assoc_el_ *prev;
+ int next_chain;
+ struct r_assoc_el_ *next;
+} r_assoc_iterator;
+
+int r_assoc_init_iter(r_assoc *assoc,r_assoc_iterator *);
+int r_assoc_iter(r_assoc_iterator *iter,void **key,int *keyl, void **val);
+int r_assoc_iter_delete(r_assoc_iterator *);
+
+int r_assoc_num_elements(r_assoc *assoc,int *sizep);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_common.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_common.h
new file mode 100644
index 000000000..c11bb3d0f
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_common.h
@@ -0,0 +1,100 @@
+/**
+ r_common.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_common.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_common.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Dec 22 10:40:07 1998
+ */
+
+
+#ifndef _r_common_h
+#define _r_common_h
+
+#include "r_defaults.h"
+#include "r_includes.h"
+#include "r_types.h"
+#include "r_macros.h"
+#include "r_errors.h"
+#include "r_data.h"
+
+/* defines for possibly replaced functions */
+#ifndef HAVE_STRDUP
+char *strdup(char *in);
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c
new file mode 100644
index 000000000..f1aca53ec
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c
@@ -0,0 +1,177 @@
+/**
+ r_crc32.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ crc32.c
+
+ Copyright (C) 2003, RTFM, Inc.
+ All Rights Reserved.
+
+ ekr@rtfm.com Fri Jan 31 13:57:43 2003
+
+ THE FOLLOWING CODE WAS EXTRACTED FROM FreeBSD
+ The comment below was the original header
+
+ The main function was modified to process a buffer
+ rather than a file
+ */
+
+/*
+ * This code implements the AUTODIN II polynomial used by Ethernet,
+ * and can be used to calculate multicast address hash indices.
+ * It assumes that the low order bits will be transmitted first,
+ * and consequently the low byte should be sent first when
+ * the crc computation is finished. The crc should be complemented
+ * before transmission.
+ * The variable corresponding to the macro argument "crc" should
+ * be an unsigned long and should be preset to all ones for Ethernet
+ * use. An error-free packet will leave 0xDEBB20E3 in the crc.
+ * Spencer Garrett <srg@quick.com>
+ */
+
+
+#include <sys/types.h>
+#include <r_types.h>
+
+static char *RCSSTRING __UNUSED__ ="$Id: r_crc32.c,v 1.4 2008/11/26 03:22:02 adamcain Exp $";
+
+#ifdef WIN32
+#define u_int32_t UINT4
+#endif
+
+#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ * x^32 + x^26 + x^23 + x^22 + x^16 +
+ * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+static const u_int32_t crctab[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+#include <stdio.h>
+#include <sys/types.h>
+
+
+int r_crc32 (char *buf,int dlen,u_int32_t *cval);
+
+int
+r_crc32(buf, dlen, cval)
+ char *buf;
+ int dlen;
+ u_int32_t *cval;
+{
+ u_int32_t crc = ~0;
+ char *p ;
+ int i;
+ u_int32_t crc32_total = 0 ;
+
+ p=buf;
+
+ for(i=0;i<dlen;i++){
+ CRC(crc, *p) ;
+ CRC(crc32_total, *p) ;
+ p++;
+ }
+
+ *cval = ~crc ;
+
+ return 0 ;
+}
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.h
new file mode 100644
index 000000000..c11be4e04
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.h
@@ -0,0 +1,14 @@
+/**
+ r_crc32.h
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ All Rights Reserved.
+
+ */
+
+#ifndef _r_crc32_
+#define _r_crc32_
+
+int r_crc32 (char *buf,int dlen, UINT4 *cval);
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_data.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_data.c
new file mode 100644
index 000000000..add527575
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_data.c
@@ -0,0 +1,249 @@
+/**
+ r_data.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_data.c
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_data.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+ ekr@rtfm.com Tue Aug 17 15:39:50 1999
+ */
+
+
+static char *RCSSTRING __UNUSED__ =(char *)"$Id: r_data.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $";
+
+#include <string.h>
+#include <r_common.h>
+#include <r_data.h>
+#include <string.h>
+
+int r_data_create(dp,d,l)
+ Data **dp;
+ const UCHAR *d;
+ int l;
+ {
+ Data *d_=0;
+ int _status;
+
+ if(!(d_=(Data *)RCALLOC(sizeof(Data))))
+ ABORT(R_NO_MEMORY);
+ if(!(d_->data=(UCHAR *)RMALLOC(l)))
+ ABORT(R_NO_MEMORY);
+
+ memcpy(d_->data,d,l);
+ d_->len=l;
+
+ *dp=d_;
+
+ _status=0;
+ abort:
+ if(_status)
+ r_data_destroy(&d_);
+
+ return(_status);
+ }
+
+
+int r_data_alloc_mem(d,l)
+ Data *d;
+ int l;
+ {
+ int _status;
+
+ if(!(d->data=(UCHAR *)RMALLOC(l)))
+ ABORT(R_NO_MEMORY);
+ d->len=l;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int r_data_alloc(dp,l)
+ Data **dp;
+ int l;
+ {
+ Data *d_=0;
+ int _status;
+
+ if(!(d_=(Data *)RCALLOC(sizeof(Data))))
+ ABORT(R_NO_MEMORY);
+ if(!(d_->data=(UCHAR *)RCALLOC(l)))
+ ABORT(R_NO_MEMORY);
+
+ d_->len=l;
+
+ *dp=d_;
+ _status=0;
+ abort:
+ if(_status)
+ r_data_destroy(&d_);
+
+ return(_status);
+ }
+
+int r_data_make(dp,d,l)
+ Data *dp;
+ const UCHAR *d;
+ int l;
+ {
+ if(!(dp->data=(UCHAR *)RMALLOC(l)))
+ ERETURN(R_NO_MEMORY);
+
+ memcpy(dp->data,d,l);
+ dp->len=l;
+
+ return(0);
+ }
+
+int r_data_destroy(dp)
+ Data **dp;
+ {
+ if(!dp || !*dp)
+ return(0);
+
+ if((*dp)->data)
+ RFREE((*dp)->data);
+
+ RFREE(*dp);
+ *dp=0;
+
+ return(0);
+ }
+
+int r_data_destroy_v(v)
+ void *v;
+ {
+ Data *d;
+
+ if(!v)
+ return(0);
+
+ d=(Data *)v;
+ r_data_zfree(d);
+
+ RFREE(d);
+
+ return(0);
+ }
+
+int r_data_destroy_vp(v)
+ void **v;
+ {
+ Data *d;
+
+ if(!v || !*v)
+ return(0);
+
+ d=(Data *)*v;
+ r_data_zfree(d);
+
+ *v=0;
+ RFREE(d);
+
+ return(0);
+ }
+
+int r_data_copy(dst,src)
+ Data *dst;
+ Data *src;
+ {
+ if(!(dst->data=(UCHAR *)RMALLOC(src->len)))
+ ERETURN(R_NO_MEMORY);
+ memcpy(dst->data,src->data,dst->len=src->len);
+ return(0);
+ }
+
+int r_data_zfree(d)
+ Data *d;
+ {
+ if(!d)
+ return(0);
+ if(!d->data)
+ return(0);
+ memset(d->data,0,d->len);
+ RFREE(d->data);
+ return(0);
+ }
+
+int r_data_compare(d1,d2)
+ Data *d1;
+ Data *d2;
+ {
+ if(d1->len<d2->len)
+ return(-1);
+ if(d2->len<d1->len)
+ return(-1);
+ return(memcmp(d1->data,d2->data,d1->len));
+ }
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_data.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_data.h
new file mode 100644
index 000000000..0e46c3196
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_data.h
@@ -0,0 +1,108 @@
+/**
+ r_data.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_data.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_data.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Wed Feb 10 14:18:19 1999
+ */
+
+
+#ifndef _r_data_h
+#define _r_data_h
+
+typedef struct Data_ {
+ UCHAR *data;
+ int len;
+} Data;
+
+int r_data_create(Data **dp,const UCHAR *d,int l);
+int r_data_alloc(Data **dp, int l);
+int r_data_make(Data *dp, const UCHAR *d,int l);
+int r_data_alloc_mem(Data *d,int l);
+int r_data_destroy(Data **dp);
+int r_data_destroy_v(void *v);
+int r_data_destroy_vp(void **vp);
+int r_data_copy(Data *dst,Data *src);
+int r_data_zfree(Data *d);
+int r_data_compare(Data *d1,Data *d2);
+
+#define INIT_DATA(a,b,c) (a).data=b; (a).len=c
+#define ATTACH_DATA(a,b) (a).data=b; (a).len=sizeof(b)
+#define ZERO_DATA(a) (a).data=0; (a).len=0
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_defaults.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_defaults.h
new file mode 100644
index 000000000..0ec91a033
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_defaults.h
@@ -0,0 +1,91 @@
+/**
+ r_defaults.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_defaults.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_defaults.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Dec 22 10:39:14 1998
+ */
+
+
+#ifndef _r_defaults_h
+#define _r_defaults_h
+
+/*The needs defines don't belong here*/
+#define R_NEEDS_STDLIB_H
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.c
new file mode 100644
index 000000000..93203ed02
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.c
@@ -0,0 +1,139 @@
+/**
+ r_errors.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_errors.c
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_errors.c,v 1.5 2008/11/26 03:22:02 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Feb 16 16:37:05 1999
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: r_errors.c,v 1.5 2008/11/26 03:22:02 adamcain Exp $";
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "r_common.h"
+#include "r_errors.h"
+
+static struct {
+ int errnum;
+ char *str;
+} errors[] = NR_ERROR_MAPPING;
+
+int nr_verr_exit(char *fmt,...)
+ {
+ va_list ap;
+
+ va_start(ap,fmt);
+ vfprintf(stderr,fmt,ap);
+
+ if (fmt[0] != '\0' && fmt[strlen(fmt)-1] != '\n')
+ fprintf(stderr,"\n");
+
+ exit(1);
+ }
+
+char *
+nr_strerror(int errnum)
+{
+ static char unknown_error[256];
+ int i;
+ char *error = 0;
+
+ for (i = 0; i < sizeof(errors)/sizeof(*errors); ++i) {
+ if (errnum == errors[i].errnum) {
+ error = errors[i].str;
+ break;
+ }
+ }
+
+ if (! error) {
+ snprintf(unknown_error, sizeof(unknown_error), "Unknown error: %d", errnum);
+ error = unknown_error;
+ }
+
+ return error;
+}
+
+int
+nr_strerror_r(int errnum, char *strerrbuf, size_t buflen)
+{
+ char *error = nr_strerror(errnum);
+ snprintf(strerrbuf, buflen, "%s", error);
+ return 0;
+}
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.h
new file mode 100644
index 000000000..f52375b98
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_errors.h
@@ -0,0 +1,127 @@
+/**
+ r_errors.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_errors.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_errors.h,v 1.4 2007/10/12 20:53:24 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Dec 22 10:59:49 1998
+ */
+
+
+#ifndef _r_errors_h
+#define _r_errors_h
+
+#define R_NO_MEMORY 1 /*out of memory*/
+#define R_NOT_FOUND 2 /*Item not found*/
+#define R_INTERNAL 3 /*Unspecified internal error*/
+#define R_ALREADY 4 /*Action already done*/
+#define R_EOD 5 /*end of data*/
+#define R_BAD_ARGS 6 /*Bad arguments*/
+#define R_BAD_DATA 7 /*Bad data*/
+#define R_WOULDBLOCK 8 /*Operation would block */
+#define R_QUEUED 9 /*Operation was queued */
+#define R_FAILED 10 /*Operation failed */
+#define R_REJECTED 11 /* We don't care about this */
+#define R_INTERRUPTED 12 /* Operation interrupted */
+#define R_IO_ERROR 13 /* I/O Error */
+#define R_NOT_PERMITTED 14 /* Permission denied */
+#define R_RETRY 15 /* Retry possible */
+
+#define NR_ERROR_MAPPING {\
+ { R_NO_MEMORY, "Cannot allocate memory" },\
+ { R_NOT_FOUND, "Item not found" },\
+ { R_INTERNAL, "Internal failure" },\
+ { R_ALREADY, "Action already performed" },\
+ { R_EOD, "End of data" },\
+ { R_BAD_ARGS, "Invalid argument" },\
+ { R_BAD_DATA, "Invalid data" },\
+ { R_WOULDBLOCK, "Operation would block" },\
+ { R_QUEUED, "Operation queued" },\
+ { R_FAILED, "Operation failed" },\
+ { R_REJECTED, "Operation rejected" },\
+ { R_INTERRUPTED, "Operation interrupted" },\
+ { R_IO_ERROR, "I/O error" },\
+ { R_NOT_PERMITTED, "Permission Denied" },\
+ { R_RETRY, "Retry may be possible" },\
+ }
+
+int nr_verr_exit(char *fmt,...);
+
+char *nr_strerror(int errnum);
+int nr_strerror_r(int errnum, char *strerrbuf, size_t buflen);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_includes.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_includes.h
new file mode 100644
index 000000000..4fb7af564
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_includes.h
@@ -0,0 +1,98 @@
+/**
+ r_includes.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_includes.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_includes.h,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Dec 22 11:38:50 1998
+ */
+
+
+#ifndef _r_includes_h
+#define _r_includes_h
+
+#ifdef R_NEEDS_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef R_NEEDS_MEMORY_H
+#include <memory.h>
+#endif
+
+#include <stdio.h>
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_list.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_list.c
new file mode 100644
index 000000000..a880f9933
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_list.c
@@ -0,0 +1,276 @@
+/**
+ r_list.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_list.c
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_list.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Jan 19 08:36:39 1999
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: r_list.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $";
+
+#include <r_common.h>
+#include "r_list.h"
+
+typedef struct r_list_el_ {
+ void *data;
+ struct r_list_el_ *next;
+ struct r_list_el_ *prev;
+ int (*copy)(void **new,void *old);
+ int (*destroy)(void **ptr);
+} r_list_el;
+
+struct r_list_ {
+ struct r_list_el_ *first;
+ struct r_list_el_ *last;
+};
+
+int r_list_create(listp)
+ r_list **listp;
+ {
+ r_list *list=0;
+ int _status;
+
+ if(!(list=(r_list *)RCALLOC(sizeof(r_list))))
+ ABORT(R_NO_MEMORY);
+
+ list->first=0;
+ list->last=0;
+ *listp=list;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int r_list_destroy(listp)
+ r_list **listp;
+ {
+ r_list *list;
+ r_list_el *el;
+
+ if(!listp || !*listp)
+ return(0);
+ list=*listp;
+
+ el=list->first;
+
+ while(el){
+ r_list_el *el_t;
+
+ if(el->destroy && el->data)
+ el->destroy(&el->data);
+ el_t=el;
+ el=el->next;
+ RFREE(el_t);
+ }
+
+ RFREE(list);
+ *listp=0;
+
+ return(0);
+ }
+
+int r_list_copy(outp,in)
+ r_list**outp;
+ r_list *in;
+ {
+ r_list *out=0;
+ r_list_el *el,*el2,*last=0;
+ int r, _status;
+
+ if(!in){
+ *outp=0;
+ return(0);
+ }
+
+ if(r=r_list_create(&out))
+ ABORT(r);
+
+ for(el=in->first;el;el=el->next){
+ if(!(el2=(r_list_el *)RCALLOC(sizeof(r_list_el))))
+ ABORT(R_NO_MEMORY);
+
+ if(el->copy && el->data){
+ if(r=el->copy(&el2->data,el->data))
+ ABORT(r);
+ }
+
+ el2->copy=el->copy;
+ el2->destroy=el->destroy;
+
+ if(!(out->first))
+ out->first=el2;
+
+ el2->prev=last;
+ if(last) last->next=el2;
+ last=el2;
+ }
+
+ out->last=last;
+
+ *outp=out;
+
+ _status=0;
+ abort:
+ if(_status)
+ r_list_destroy(&out);
+ return(_status);
+ }
+
+int r_list_insert(list,value,copy,destroy)
+ r_list *list;
+ void *value;
+ int (*copy)(void **out, void *in);
+ int (*destroy)(void **val);
+ {
+ r_list_el *el=0;
+ int _status;
+
+ if(!(el=(r_list_el *)RCALLOC(sizeof(r_list_el))))
+ ABORT(R_NO_MEMORY);
+ el->data=value;
+ el->copy=copy;
+ el->destroy=destroy;
+
+ el->prev=0;
+ el->next=list->first;
+ if(list->first){
+ list->first->prev=el;
+ }
+ list->first=el;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int r_list_append(list,value,copy,destroy)
+ r_list *list;
+ void *value;
+ int (*copy)(void **out, void *in);
+ int (*destroy)(void **val);
+ {
+ r_list_el *el=0;
+ int _status;
+
+ if(!(el=(r_list_el *)RCALLOC(sizeof(r_list_el))))
+ ABORT(R_NO_MEMORY);
+ el->data=value;
+ el->copy=copy;
+ el->destroy=destroy;
+
+ el->prev=list->last;
+ el->next=0;
+
+ if(list->last) list->last->next=el;
+ else list->first=el;
+
+ list->last=el;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int r_list_init_iter(list,iter)
+ r_list *list;
+ r_list_iterator *iter;
+ {
+ iter->list=list;
+ iter->ptr=list->first;
+
+ return(0);
+ }
+
+int r_list_iter(iter,val)
+ r_list_iterator *iter;
+ void **val;
+ {
+ if(!iter->ptr)
+ return(R_EOD);
+
+ *val=iter->ptr->data;
+ iter->ptr=iter->ptr->next;
+
+ return(0);
+ }
+
+
+
+
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_list.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_list.h
new file mode 100644
index 000000000..bbefc6c39
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_list.h
@@ -0,0 +1,106 @@
+/**
+ r_list.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_list.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_list.h,v 1.3 2007/06/08 17:41:49 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Jan 19 08:36:48 1999
+ */
+
+
+#ifndef _r_list_h
+#define _r_list_h
+
+typedef struct r_list_ r_list;
+
+typedef struct r_list_iterator_ {
+ r_list *list;
+ struct r_list_el_ *ptr;
+} r_list_iterator;
+
+int r_list_create(r_list **listp);
+int r_list_destroy(r_list **listp);
+int r_list_copy(r_list **out,r_list *in);
+int r_list_insert(r_list *list,void *value,
+ int (*copy)(void **knew,void *old),
+ int (*destroy)(void **ptr));
+int r_list_append(r_list *list,void *value,
+ int (*copy)(void **knew,void *old),
+ int (*destroy)(void **ptr));
+int r_list_init_iter(r_list *list,r_list_iterator *iter);
+int r_list_iter(r_list_iterator *iter,void **val);
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h
new file mode 100644
index 000000000..cdae0c180
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h
@@ -0,0 +1,137 @@
+/**
+ r_macros.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_macros.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_macros.h,v 1.3 2007/06/26 22:37:57 adamcain Exp $
+
+ ekr@rtfm.com Tue Dec 22 10:37:32 1998
+ */
+
+
+#ifndef _r_macros_h
+#define _r_macros_h
+
+/* Haven't removed all PROTO_LIST defs yet */
+#define PROTO_LIST(a) a
+
+#ifndef WIN32
+#ifndef __GNUC__
+#define __FUNCTION__ "unknown"
+#endif
+#endif
+
+#ifdef R_TRACE_ERRORS
+#ifdef WIN32
+#define REPORT_ERROR_(caller,a) printf("%s: error %d at %s:%d (function %s)\n", \
+ caller,a,__FILE__,__LINE__,__FUNCTION__)
+#else
+#define REPORT_ERROR_(caller,a) fprintf(stderr,"%s: error %d at %s:%d (function %s)\n", \
+ caller,a,__FILE__,__LINE__,__FUNCTION__)
+#endif
+#else
+#define REPORT_ERROR_(caller,a)
+#endif
+
+#ifndef ERETURN
+#define ERETURN(a) do {int _r=a; if(!_r) _r=-1; REPORT_ERROR_("ERETURN",_r); return(_r);} while(0)
+#endif
+
+#ifndef ABORT
+#define ABORT(a) do { int _r=a; if(!_r) _r=-1; REPORT_ERROR_("ABORT",_r); _status=_r; goto abort;} while(0)
+#endif
+
+#ifndef FREE
+#define FREE(a) if(a) free(a)
+#endif
+#ifndef MIN
+#define MIN(a,b) ((a)>(b))?(b):(a)
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((b)>(a))?(b):(a)
+#endif
+
+#ifdef DEBUG
+#define DBG(a) debug a
+int debug(int cls, char *format,...);
+#else
+#define DBG(a)
+#endif
+
+#define UNIMPLEMENTED do { fprintf(stderr,"%s:%d Function %s unimplemented\n",__FILE__,__LINE__,__FUNCTION__); abort(); } while(0)
+
+#include "r_memory.h"
+
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.c
new file mode 100644
index 000000000..a5c709d41
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.c
@@ -0,0 +1,201 @@
+/**
+ r_memory.c
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Thu Apr 22 20:40:45 2004
+ */
+
+
+static char *RCSSTRING __UNUSED__="$Id: r_memory.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $";
+
+#include <string.h>
+#include <stddef.h>
+#include <assert.h>
+#include "r_common.h"
+#include "r_memory.h"
+
+typedef struct r_malloc_chunk_ {
+#ifdef SANITY_CHECKS
+ UINT4 hdr;
+#endif
+ UCHAR type;
+ UINT4 size;
+ UCHAR memory[1];
+} r_malloc_chunk;
+
+#define CHUNK_MEMORY_OFFSET offsetof(struct r_malloc_chunk_, memory)
+#define GET_CHUNK_ADDR_FROM_MEM_ADDR(memp) \
+ ((struct r_malloc_chunk *)(((unsigned char*)(memp))-CHUNK_MEMORY_OFFSET))
+#define CHUNK_SIZE(size) (size+sizeof(r_malloc_chunk))
+
+#define HDR_FLAG 0x464c4147
+
+static UINT4 mem_usage; /* Includes our header */
+static UINT4 mem_stats[256]; /* Does not include our header */
+
+void *r_malloc(type,size)
+ int type;
+ size_t size;
+ {
+ size_t total;
+ r_malloc_chunk *chunk;
+
+ total=size+sizeof(r_malloc_chunk);
+
+ if(!(chunk=malloc(total)))
+ return(0);
+
+#ifdef SANITY_CHECKS
+ chunk->hdr=HDR_FLAG;
+#endif
+ chunk->type=type;
+ chunk->size=size;
+
+ mem_usage+=CHUNK_SIZE(size);
+ mem_stats[type]+=size;
+
+ return(chunk->memory);
+ }
+
+void *r_calloc(type,number,size)
+ int type;
+ size_t number;
+ size_t size;
+ {
+ void *ret;
+ size_t total;
+
+ total=number*size;
+
+ if(!(ret=r_malloc(type,total)))
+ return(0);
+
+ memset(ret,0,size);
+
+ return(ret);
+ }
+
+void r_free(ptr)
+ void *ptr;
+ {
+ r_malloc_chunk *chunk;
+
+ if(!ptr) return;
+
+ chunk=(r_malloc_chunk *)GET_CHUNK_ADDR_FROM_MEM_ADDR(ptr);
+#ifdef SANITY_CHECKS
+ assert(chunk->hdr==HDR_FLAG);
+#endif
+
+ mem_usage-=CHUNK_SIZE(chunk->size);
+ mem_stats[chunk->type]-=chunk->size;
+
+ free(chunk);
+ }
+
+void *r_realloc(ptr,size)
+ void *ptr;
+ size_t size;
+ {
+ r_malloc_chunk *chunk,*nchunk;
+ size_t total;
+
+ if(!ptr) return(r_malloc(255,size));
+
+ chunk=(r_malloc_chunk *)GET_CHUNK_ADDR_FROM_MEM_ADDR(ptr);
+#ifdef SANITY_CHECKS
+ assert(chunk->hdr==HDR_FLAG);
+#endif
+
+ total=size + sizeof(r_malloc_chunk);
+
+ if(!(nchunk=realloc(chunk,total)))
+ return(0);
+
+ mem_usage-=CHUNK_SIZE(nchunk->size);
+ mem_stats[nchunk->type]-=nchunk->size;
+
+ nchunk->size=size;
+ mem_usage+=CHUNK_SIZE(nchunk->size);
+ mem_stats[nchunk->type]+=nchunk->size;
+
+ return(nchunk->memory);
+ }
+
+char *r_strdup(str)
+ const char *str;
+ {
+ int len;
+ char *nstr;
+
+ if(!str)
+ return(0);
+
+ len=strlen(str)+1;
+
+ if(!(nstr=r_malloc(0,len)))
+ return(0);
+
+ memcpy(nstr,str,len);
+
+ return(nstr);
+ }
+
+int r_mem_get_usage(usagep)
+ UINT4 *usagep;
+ {
+ *usagep=mem_usage;
+
+ return(0);
+ }
+
+int r_memory_dump_stats()
+ {
+ int i;
+
+ printf("Total memory usage: %d\n",mem_usage);
+ printf("Memory usage by bucket\n");
+ for(i=0;i<256;i++){
+ if(mem_stats[i]){
+ printf("%d\t%d\n",i,mem_stats[i]);
+ }
+ }
+ return(0);
+ }
+
+void *r_malloc_compat(size)
+ size_t size;
+ {
+ return(r_malloc(255,size));
+ }
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.h
new file mode 100644
index 000000000..435707076
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_memory.h
@@ -0,0 +1,101 @@
+/**
+ r_memory.h
+
+
+ Copyright (C) 2004, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Sat Apr 24 08:30:00 2004
+ */
+
+
+#ifndef _r_memory_h
+#define _r_memory_h
+
+#define R_MALLOC_X 2
+
+#include "r_types.h"
+
+void *r_malloc(int type, size_t size);
+void *r_malloc_compat(size_t size);
+void *r_calloc(int type,size_t number,size_t size);
+void r_free (void *ptr);
+void *r_realloc(void *ptr,size_t size);
+char *r_strdup(const char *str);
+int r_mem_get_usage(UINT4 *usage);
+int r_memory_dump_stats(void);
+
+#ifdef NO_MALLOC_REPLACE
+
+#ifndef RMALLOC
+#define RMALLOC(a) malloc(a)
+#endif
+
+#ifndef RCALLOC
+#define RCALLOC(a) calloc(1,a)
+#endif
+
+#ifndef RFREE
+#define RFREE(a) if(a) free(a)
+#endif
+
+#ifndef RREALLOC
+#define RREALLOC(a,b) realloc(a,b)
+#endif
+
+#else
+
+
+#ifndef R_MALLOC_TYPE
+#define R_MALLOC_TYPE 0
+#endif
+
+#ifndef RMALLOC
+#define RMALLOC(a) r_malloc(R_MALLOC_TYPE,a)
+#endif
+
+#ifndef RCALLOC
+#define RCALLOC(a) r_calloc(R_MALLOC_TYPE,1,a)
+#endif
+
+#ifndef RFREE
+#define RFREE(a) if(a) r_free(a)
+#endif
+
+#ifndef RREALLOC
+#define RREALLOC(a,b) r_realloc(a,b)
+#endif
+
+#endif
+
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_replace.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_replace.c
new file mode 100644
index 000000000..eb0138775
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_replace.c
@@ -0,0 +1,110 @@
+/**
+ r_replace.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_replace.c
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_replace.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $
+
+
+ ekr@rtfm.com Sun Oct 1 11:18:49 2000
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: r_replace.c,v 1.2 2006/08/16 19:39:17 adamcain Exp $";
+
+#include "r_common.h"
+
+#ifndef HAVE_STRDUP
+
+char *strdup(str)
+ char *str;
+ {
+ int len=strlen(str);
+ char *n;
+
+ if(!(n=(char *)malloc(len+1)))
+ return(0);
+
+ memcpy(n,str,len+1);
+
+ return(n);
+ }
+#endif
+
+
+#ifdef SUPPLY_ATEXIT
+int atexit(void (*func)(void)){
+ ;
+}
+#endif
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_thread.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_thread.h
new file mode 100644
index 000000000..212900bcc
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_thread.h
@@ -0,0 +1,68 @@
+/**
+ r_thread.h
+
+
+ Copyright (C) 1999, RTFM, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Tue Feb 23 14:58:36 1999
+ */
+
+
+#ifndef _r_thread_h
+#define _r_thread_h
+
+typedef void *r_thread;
+typedef void *r_rwlock;
+typedef void * r_cond;
+
+int r_thread_fork (void (*func)(void *),void *arg,
+ r_thread *tid);
+int r_thread_destroy (r_thread tid);
+int r_thread_yield (void);
+int r_thread_exit (void);
+int r_thread_wait_last (void);
+int r_thread_self (void);
+
+int r_rwlock_create (r_rwlock **lockp);
+int r_rwlock_destroy (r_rwlock **lock);
+int r_rwlock_lock (r_rwlock *lock,int action);
+
+int r_cond_init (r_cond *cond);
+int r_cond_wait (r_cond cond);
+int r_cond_signal (r_cond cond);
+
+#define R_RWLOCK_UNLOCK 0
+#define R_RWLOCK_RLOCK 1
+#define R_RWLOCK_WLOCK 2
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.c
new file mode 100644
index 000000000..9ba901fc4
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.c
@@ -0,0 +1,238 @@
+/**
+ r_time.c
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_time.c
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_time.c,v 1.5 2008/11/26 03:22:02 adamcain Exp $
+
+ ekr@rtfm.com Thu Mar 4 08:43:46 1999
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: r_time.c,v 1.5 2008/11/26 03:22:02 adamcain Exp $";
+
+#include <r_common.h>
+#include <r_time.h>
+
+/*Note that t1 must be > t0 */
+int r_timeval_diff(t1,t0,diff)
+ struct timeval *t1;
+ struct timeval *t0;
+ struct timeval *diff;
+ {
+ long d;
+
+ if(t0->tv_sec > t1->tv_sec)
+ ERETURN(R_BAD_ARGS);
+ if((t0->tv_sec == t1->tv_sec) && (t0->tv_usec > t1->tv_usec))
+ ERETURN(R_BAD_ARGS);
+
+ /*Easy case*/
+ if(t0->tv_usec <= t1->tv_usec){
+ diff->tv_sec=t1->tv_sec - t0->tv_sec;
+ diff->tv_usec=t1->tv_usec - t0->tv_usec;
+ return(0);
+ }
+
+ /*Hard case*/
+ d=t0->tv_usec - t1->tv_usec;
+ if(t1->tv_sec < (t0->tv_sec + 1))
+ ERETURN(R_BAD_ARGS);
+ diff->tv_sec=t1->tv_sec - (t0->tv_sec + 1);
+ diff->tv_usec=1000000 - d;
+
+ return(0);
+ }
+
+int r_timeval_add(t1,t2,sum)
+ struct timeval *t1;
+ struct timeval *t2;
+ struct timeval *sum;
+ {
+ long tv_sec,tv_usec,d;
+
+ tv_sec=t1->tv_sec + t2->tv_sec;
+
+ d=t1->tv_usec + t2->tv_usec;
+ if(d>1000000){
+ tv_sec++;
+ tv_usec=d-1000000;
+ }
+ else{
+ tv_usec=d;
+ }
+
+ sum->tv_sec=tv_sec;
+ sum->tv_usec=tv_usec;
+
+ return(0);
+ }
+
+int r_timeval_cmp(t1,t2)
+ struct timeval *t1;
+ struct timeval *t2;
+ {
+ if(t1->tv_sec>t2->tv_sec)
+ return(1);
+ if(t1->tv_sec<t2->tv_sec)
+ return(-1);
+ if(t1->tv_usec>t2->tv_usec)
+ return(1);
+ if(t1->tv_usec<t2->tv_usec)
+ return(-1);
+ return(0);
+ }
+
+
+UINT8 r_timeval2int(tv)
+ struct timeval *tv;
+ {
+ UINT8 r=0;
+
+ r=(tv->tv_sec);
+ r*=1000000;
+ r+=tv->tv_usec;
+
+ return r;
+ }
+
+int r_int2timeval(UINT8 t,struct timeval *tv)
+ {
+ tv->tv_sec=t/1000000;
+ tv->tv_usec=t%1000000;
+
+ return(0);
+ }
+
+UINT8 r_gettimeint()
+ {
+ struct timeval tv;
+
+ gettimeofday(&tv,0);
+
+ return r_timeval2int(&tv);
+ }
+
+/* t1-t0 in microseconds */
+int r_timeval_diff_usec(struct timeval *t1, struct timeval *t0, INT8 *diff)
+ {
+ int r,_status;
+ int sign;
+ struct timeval tmp;
+
+ sign = 1;
+ if (r=r_timeval_diff(t1, t0, &tmp)) {
+ if (r == R_BAD_ARGS) {
+ sign = -1;
+ if (r=r_timeval_diff(t0, t1, &tmp))
+ ABORT(r);
+ }
+ }
+
+ /* 1 second = 1000 milliseconds
+ * 1 milliseconds = 1000 microseconds */
+
+ *diff = ((tmp.tv_sec * (1000*1000)) + tmp.tv_usec) * sign;
+
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
+/* t1-t0 in milliseconds */
+int r_timeval_diff_ms(struct timeval *t1, struct timeval *t0, INT8 *diff)
+ {
+ int r,_status;
+ int sign;
+ struct timeval tmp;
+
+ sign = 1;
+ if (r=r_timeval_diff(t1, t0, &tmp)) {
+ if (r == R_BAD_ARGS) {
+ sign = -1;
+ if (r=r_timeval_diff(t0, t1, &tmp))
+ ABORT(r);
+ }
+ }
+
+ /* 1 second = 1000 milliseconds
+ * 1 milliseconds = 1000 microseconds */
+
+ *diff = ((tmp.tv_sec * 1000) + (tmp.tv_usec / 1000)) * sign;
+
+ _status = 0;
+ abort:
+ return(_status);
+ }
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h
new file mode 100644
index 000000000..1ee1e778f
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h
@@ -0,0 +1,109 @@
+/**
+ r_time.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_time.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_time.h,v 1.4 2007/06/26 22:37:57 adamcain Exp $
+
+
+ ekr@rtfm.com Thu Mar 4 08:45:41 1999
+ */
+
+
+#ifndef _r_time_h
+#define _r_time_h
+
+#include <csi_platform.h>
+
+#ifndef WIN32
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+int r_timeval_diff(struct timeval *t1,struct timeval *t0, struct timeval *diff);
+int r_timeval_add(struct timeval *t1,struct timeval *t2, struct timeval *sum);
+int r_timeval_cmp(struct timeval *t1,struct timeval *t2);
+
+UINT8 r_timeval2int(struct timeval *tv);
+int r_int2timeval(UINT8 t,struct timeval *tv);
+UINT8 r_gettimeint(void);
+
+/* t1-t0 in microseconds */
+int r_timeval_diff_usec(struct timeval *t1, struct timeval *t0, INT8 *diff);
+
+/* t1-t0 in milliseconds */
+int r_timeval_diff_ms(struct timeval *t1, struct timeval *t0, INT8 *diff);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_types.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_types.h
new file mode 100644
index 000000000..d00810a7a
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_types.h
@@ -0,0 +1,213 @@
+/**
+ r_types.h
+
+
+ Copyright (C) 2002-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ */
+
+/**
+ r_types.h
+
+
+ Copyright (C) 1999-2000 RTFM, Inc.
+ All Rights Reserved
+
+ This package is a SSLv3/TLS protocol analyzer written by Eric Rescorla
+ <ekr@rtfm.com> and licensed by RTFM, Inc.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed by Eric Rescorla for
+ RTFM, Inc.
+
+ 4. Neither the name of RTFM, Inc. nor the name of Eric Rescorla may be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ERIC RESCORLA AND RTFM, INC. ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY SUCH DAMAGE.
+
+ $Id: r_types.h,v 1.2 2006/08/16 19:39:18 adamcain Exp $
+
+
+ ekr@rtfm.com Tue Dec 22 10:36:02 1998
+ */
+
+
+#ifndef _r_types_h
+#define _r_types_h
+
+/* Either define R_PLATFORM_INT_TYPES or be on a platform that
+ has stdint.h */
+#ifdef R_PLATFORM_INT_TYPES
+#include R_PLATFORM_INT_TYPES
+#else
+#include <stdint.h>
+#endif
+
+#ifndef R_DEFINED_INT2
+#ifndef SIZEOF_INT
+typedef short INT2;
+#else
+# if (SIZEOF_INT==2)
+typedef int INT2;
+# elif (SIZEOF_SHORT==2)
+typedef short INT2;
+# elif (SIZEOF_LONG==2)
+typedef long INT2;
+# else
+# error no type for INT2
+# endif
+#endif
+#else
+typedef R_DEFINED_INT2 INT2;
+#endif
+
+#ifndef R_DEFINED_UINT2
+#ifndef SIZEOF_UNSIGNED_INT
+typedef unsigned short UINT2;
+#else
+# if (SIZEOF_UNSIGNED_INT==2)
+typedef unsigned int UINT2;
+# elif (SIZEOF_UNSIGNED_SHORT==2)
+typedef unsigned short UINT2;
+# elif (SIZEOF_UNSIGNED_LONG==2)
+typedef unsigned long UINT2;
+# else
+# error no type for UINT2
+# endif
+#endif
+#else
+typedef R_DEFINED_UINT2 UINT2;
+#endif
+
+#ifndef R_DEFINED_INT4
+#ifndef SIZEOF_INT
+typedef int INT4;
+#else
+# if (SIZEOF_INT==4)
+typedef int INT4;
+# elif (SIZEOF_SHORT==4)
+typedef short INT4;
+# elif (SIZEOF_LONG==4)
+typedef long INT4;
+# else
+# error no type for INT4
+# endif
+#endif
+#else
+typedef R_DEFINED_INT4 INT4;
+#endif
+
+#ifndef R_DEFINED_UINT4
+#ifndef SIZEOF_UNSIGNED_INT
+typedef unsigned int UINT4;
+#else
+# if (SIZEOF_UNSIGNED_INT==4)
+typedef unsigned int UINT4;
+# elif (SIZEOF_UNSIGNED_SHORT==4)
+typedef unsigned short UINT4;
+# elif (SIZEOF_UNSIGNED_LONG==4)
+typedef unsigned long UINT4;
+# else
+# error no type for UINT4
+# endif
+#endif
+#else
+typedef R_DEFINED_UINT4 UINT4;
+#endif
+
+#ifndef R_DEFINED_INT8
+#ifndef SIZEOF_INT
+typedef long long INT8;
+#else
+# if (SIZEOF_INT==8)
+typedef int INT8;
+# elif (SIZEOF_SHORT==8)
+typedef short INT8;
+# elif (SIZEOF_LONG==8)
+typedef long INT8;
+# elif (SIZEOF_LONG_LONG==8)
+typedef long long INT8;
+# else
+# error no type for INT8
+# endif
+#endif
+#else
+typedef R_DEFINED_INT8 INT8;
+#endif
+
+#ifndef R_DEFINED_UINT8
+#ifndef SIZEOF_UNSIGNED_INT
+typedef unsigned long long UINT8;
+#else
+# if (SIZEOF_UNSIGNED_INT==8)
+typedef unsigned int UINT8;
+# elif (SIZEOF_UNSIGNED_SHORT==8)
+typedef unsigned short UINT8;
+# elif (SIZEOF_UNSIGNED_LONG==8)
+typedef unsigned long UINT8;
+# elif (SIZEOF_UNSIGNED_LONG_LONG==8)
+typedef unsigned long long UINT8;
+# else
+# error no type for UINT8
+# endif
+#endif
+#else
+typedef R_DEFINED_UINT8 UINT8;
+#endif
+
+#ifndef R_DEFINED_UCHAR
+typedef unsigned char UCHAR;
+#else
+typedef R_DEFINED_UCHAR UCHAR;
+#endif
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/p_buf.c b/media/mtransport/third_party/nrappkit/src/util/p_buf.c
new file mode 100644
index 000000000..8bb0e33bd
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/p_buf.c
@@ -0,0 +1,217 @@
+/**
+ p_buf.c
+
+
+ Copyright (C) 2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ All Rights Reserved.
+
+ ekr@rtfm.com Tue Nov 25 16:33:08 2003
+ */
+
+static char *RCSSTRING __UNUSED__ ="Id: p_buf.c,v 1.3 2004/01/03 22:13:53 ekr Exp $";
+
+#include <string.h>
+#include <stddef.h>
+#include "nr_common.h"
+#include "p_buf.h"
+
+
+static int nr_p_buf_destroy_chain(nr_p_buf_head *head);
+static int nr_p_buf_destroy(nr_p_buf *buf);
+
+int nr_p_buf_ctx_create(size,ctxp)
+ int size;
+ nr_p_buf_ctx **ctxp;
+ {
+ int _status;
+ nr_p_buf_ctx *ctx=0;
+
+ if(!(ctx=(nr_p_buf_ctx *)RCALLOC(sizeof(nr_p_buf_ctx))))
+ ABORT(R_NO_MEMORY);
+
+ ctx->buf_size=size;
+ STAILQ_INIT(&ctx->free_list);
+
+ *ctxp=ctx;
+ _status=0;
+ abort:
+ if(_status){
+ nr_p_buf_ctx_destroy(&ctx);
+ }
+ return(_status);
+ }
+
+int nr_p_buf_ctx_destroy(ctxp)
+ nr_p_buf_ctx **ctxp;
+ {
+ nr_p_buf_ctx *ctx;
+
+ if(!ctxp || !*ctxp)
+ return(0);
+
+ ctx=*ctxp;
+
+ nr_p_buf_destroy_chain(&ctx->free_list);
+
+ RFREE(ctx);
+ *ctxp=0;
+
+ return(0);
+ }
+
+int nr_p_buf_alloc(ctx,bufp)
+ nr_p_buf_ctx *ctx;
+ nr_p_buf **bufp;
+ {
+ int _status;
+ nr_p_buf *buf=0;
+
+ if(!STAILQ_EMPTY(&ctx->free_list)){
+ buf=STAILQ_FIRST(&ctx->free_list);
+ STAILQ_REMOVE_HEAD(&ctx->free_list,entry);
+ goto ok;
+ }
+ else {
+ if(!(buf=(nr_p_buf *)RCALLOC(sizeof(nr_p_buf))))
+ ABORT(R_NO_MEMORY);
+ if(!(buf->data=(UCHAR *)RMALLOC(ctx->buf_size)))
+ ABORT(R_NO_MEMORY);
+ buf->size=ctx->buf_size;
+ }
+
+ ok:
+ buf->r_offset=0;
+ buf->length=0;
+
+ *bufp=buf;
+ _status=0;
+ abort:
+ if(_status){
+ nr_p_buf_destroy(buf);
+ }
+ return(_status);
+ }
+
+int nr_p_buf_free(ctx,buf)
+ nr_p_buf_ctx *ctx;
+ nr_p_buf *buf;
+ {
+ STAILQ_INSERT_TAIL(&ctx->free_list,buf,entry);
+
+ return(0);
+ }
+
+int nr_p_buf_free_chain(ctx,head)
+ nr_p_buf_ctx *ctx;
+ nr_p_buf_head *head;
+ {
+ nr_p_buf *n1,*n2;
+
+ n1=STAILQ_FIRST(head);
+ while(n1){
+ n2=STAILQ_NEXT(n1,entry);
+
+ nr_p_buf_free(ctx,n1);
+
+ n1=n2;
+ }
+
+ return(0);
+ }
+
+
+int nr_p_buf_write_to_chain(ctx,chain,data,len)
+ nr_p_buf_ctx *ctx;
+ nr_p_buf_head *chain;
+ UCHAR *data;
+ UINT4 len;
+ {
+ int r,_status;
+ nr_p_buf *buf;
+
+ buf=STAILQ_LAST(chain,nr_p_buf_,entry);
+ while(len){
+ int towrite;
+
+ if(!buf){
+ if(r=nr_p_buf_alloc(ctx,&buf))
+ ABORT(r);
+ STAILQ_INSERT_TAIL(chain,buf,entry);
+ }
+
+ towrite=MIN(len,(buf->size-(buf->length+buf->r_offset)));
+
+ memcpy(buf->data+buf->length+buf->r_offset,data,towrite);
+ len-=towrite;
+ data+=towrite;
+ buf->length+=towrite;
+
+ r_log(LOG_COMMON,LOG_DEBUG,"Wrote %d bytes to buffer %p",towrite,buf);
+ buf=0;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+static int nr_p_buf_destroy_chain(head)
+ nr_p_buf_head *head;
+ {
+ nr_p_buf *n1,*n2;
+
+ n1=STAILQ_FIRST(head);
+ while(n1){
+ n2=STAILQ_NEXT(n1,entry);
+
+ nr_p_buf_destroy(n1);
+
+ n1=n2;
+ }
+
+ return(0);
+ }
+
+static int nr_p_buf_destroy(buf)
+ nr_p_buf *buf;
+ {
+ if(!buf)
+ return(0);
+
+ RFREE(buf->data);
+ RFREE(buf);
+
+ return(0);
+ }
+
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/p_buf.h b/media/mtransport/third_party/nrappkit/src/util/p_buf.h
new file mode 100644
index 000000000..29881960c
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/p_buf.h
@@ -0,0 +1,72 @@
+/**
+ p_buf.h
+
+
+ Copyright (C) 2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Tue Nov 25 15:58:37 2003
+ */
+
+
+#ifndef _p_buf_h
+#define _p_buf_h
+
+typedef struct nr_p_buf_ {
+ UCHAR *data; /*The pointer to the buffer where the data lives */
+ UINT4 size; /*The size of the buffer */
+ UINT4 r_offset; /*The offset into the buffer where the data starts
+ when reading */
+ UINT4 length; /*The length of the data portion */
+
+ STAILQ_ENTRY(nr_p_buf_) entry;
+} nr_p_buf;
+
+typedef STAILQ_HEAD(nr_p_buf_head_,nr_p_buf_) nr_p_buf_head;
+
+
+typedef struct nr_p_buf_ctx_ {
+ int buf_size;
+
+ nr_p_buf_head free_list;
+} nr_p_buf_ctx;
+
+int nr_p_buf_ctx_create(int size,nr_p_buf_ctx **ctxp);
+int nr_p_buf_ctx_destroy(nr_p_buf_ctx **ctxp);
+int nr_p_buf_alloc(nr_p_buf_ctx *ctx,nr_p_buf **bufp);
+int nr_p_buf_free(nr_p_buf_ctx *ctx,nr_p_buf *buf);
+int nr_p_buf_free_chain(nr_p_buf_ctx *ctx,nr_p_buf_head *chain);
+int nr_p_buf_write_to_chain(nr_p_buf_ctx *ctx,
+ nr_p_buf_head *chain,
+ UCHAR *data,UINT4 len);
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/util.c b/media/mtransport/third_party/nrappkit/src/util/util.c
new file mode 100644
index 000000000..31a245f45
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/util.c
@@ -0,0 +1,792 @@
+/**
+ util.c
+
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Wed Dec 26 17:19:36 2001
+ */
+
+
+static char *RCSSTRING __UNUSED__ ="$Id: util.c,v 1.5 2007/11/21 00:09:13 adamcain Exp $";
+
+#ifndef WIN32
+#include <sys/uio.h>
+#include <pwd.h>
+#include <dirent.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#ifdef OPENSSL
+#include <openssl/evp.h>
+#endif
+#include "nr_common.h"
+#include "r_common.h"
+#include "registry.h"
+#include "util.h"
+#include "r_log.h"
+
+int nr_util_default_log_facility=LOG_COMMON;
+
+int nr_get_filename(base,name,namep)
+ char *base;
+ char *name;
+ char **namep;
+ {
+ int len=strlen(base)+strlen(name)+2;
+ char *ret=0;
+ int _status;
+
+ if(!(ret=(char *)RMALLOC(len)))
+ ABORT(R_NO_MEMORY);
+ if(base[strlen(base)-1]!='/'){
+ sprintf(ret,"%s/%s",base,name);
+ }
+ else{
+ sprintf(ret,"%s%s",base,name);
+ }
+ *namep=ret;
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+#if 0
+int read_RSA_private_key(base,name,keyp)
+ char *base;
+ char *name;
+ RSA **keyp;
+ {
+ char *keyfile=0;
+ BIO *bio=0;
+ FILE *fp=0;
+ RSA *rsa=0;
+ int r,_status;
+
+ /* Load the keyfile */
+ if(r=get_filename(base,name,&keyfile))
+ ABORT(r);
+ if(!(fp=fopen(keyfile,"r")))
+ ABORT(R_NOT_FOUND);
+ if(!(bio=BIO_new(BIO_s_file())))
+ ABORT(R_NO_MEMORY);
+ BIO_set_fp(bio,fp,BIO_NOCLOSE);
+
+ if(!(rsa=PEM_read_bio_RSAPrivateKey(bio,0,0,0)))
+ ABORT(R_NOT_FOUND);
+
+ *keyp=rsa;
+ _status=0;
+ abort:
+ return(_status);
+ }
+#endif
+
+
+void nr_errprintf_log(const char *format,...)
+ {
+ va_list ap;
+
+ va_start(ap,format);
+
+ r_vlog(nr_util_default_log_facility,LOG_ERR,format,ap);
+
+ va_end(ap);
+ }
+
+void nr_errprintf_log2(void *ignore, const char *format,...)
+ {
+ va_list ap;
+
+ va_start(ap,format);
+
+ r_vlog(nr_util_default_log_facility,LOG_ERR,format,ap);
+
+ va_end(ap);
+ }
+
+
+int nr_fwrite_all(FILE *fp,UCHAR *buf,int len)
+ {
+ int r,_status;
+
+ while(len){
+ r=fwrite(buf,1,len,fp);
+ if(r==0)
+ ABORT(R_IO_ERROR);
+
+ len-=r;
+ buf+=r;
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+int nr_read_data(fd,buf,len)
+ int fd;
+ char *buf;
+ int len;
+ {
+ int r,_status;
+
+ while(len){
+ r=NR_SOCKET_READ(fd,buf,len);
+ if(r<=0)
+ ABORT(R_EOD);
+
+ buf+=r;
+ len-=r;
+ }
+
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+#ifdef WIN32
+ // TODO
+#else
+int nr_drop_privileges(char *username)
+ {
+ int _status;
+
+ /* Drop privileges */
+ if ((getuid() == 0) || geteuid()==0) {
+ struct passwd *passwd;
+
+ if ((passwd = getpwnam(CAPTURE_USER)) == 0){
+ r_log(LOG_GENERIC,LOG_EMERG,"Couldn't get user %s",CAPTURE_USER);
+ ABORT(R_INTERNAL);
+ }
+
+ if(setuid(passwd->pw_uid)!=0){
+ r_log(LOG_GENERIC,LOG_EMERG,"Couldn't drop privileges");
+ ABORT(R_INTERNAL);
+ }
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+#endif
+
+int nr_bin2hex(UCHAR *in,int len,UCHAR *out)
+ {
+ while(len){
+ sprintf((char*)out,"%.2x",in[0] & 0xff);
+
+ in+=1;
+ out+=2;
+
+ len--;
+ }
+
+ return(0);
+ }
+
+int nr_hex_ascii_dump(Data *data)
+ {
+ UCHAR *ptr=data->data;
+ int len=data->len;
+
+ while(len){
+ int i;
+ int bytes=MIN(len,16);
+
+ for(i=0;i<bytes;i++)
+ printf("%.2x ",ptr[i]&255);
+ /* Fill */
+ for(i=0;i<(16-bytes);i++)
+ printf(" ");
+ printf(" ");
+
+ for(i=0;i<bytes;i++){
+ if(isprint(ptr[i]))
+ printf("%c",ptr[i]);
+ else
+ printf(".");
+ }
+ printf("\n");
+
+ len-=bytes;
+ ptr+=bytes;
+ }
+ return(0);
+ }
+
+#ifdef OPENSSL
+int nr_sha1_file(char *filename,UCHAR *out)
+ {
+ EVP_MD_CTX md_ctx;
+ FILE *fp=0;
+ int r,_status;
+ UCHAR buf[1024];
+ int out_len;
+
+ EVP_MD_CTX_init(&md_ctx);
+
+ if(!(fp=fopen(filename,"r"))){
+ r_log(LOG_COMMON,LOG_ERR,"Couldn't open file %s",filename);
+ ABORT(R_NOT_FOUND);
+ }
+
+ EVP_DigestInit_ex(&md_ctx,EVP_sha1(),0);
+
+ while(1){
+ r=fread(buf,1,sizeof(buf),fp);
+
+ if(r<0){
+ r_log(LOG_COMMON,LOG_ERR,"Error reading from %s",filename);
+ ABORT(R_INTERNAL);
+ }
+
+ if(!r)
+ break;
+
+ EVP_DigestUpdate(&md_ctx,buf,r);
+ }
+
+ EVP_DigestFinal(&md_ctx,out,(unsigned int*)&out_len);
+ if(out_len!=20)
+ ABORT(R_INTERNAL);
+
+ _status=0;
+ abort:
+ EVP_MD_CTX_cleanup(&md_ctx);
+ if(fp) fclose(fp);
+
+ return(_status);
+ }
+
+#endif
+
+#ifdef WIN32
+ // TODO
+#else
+
+#if 0
+
+#include <fts.h>
+
+int nr_rm_tree(char *path)
+ {
+ FTS *fts=0;
+ FTSENT *p;
+ int failed=0;
+ int _status;
+ char *argv[2];
+
+ argv[0]=path;
+ argv[1]=0;
+
+ if(!(fts=fts_open(argv,0,NULL))){
+ r_log_e(LOG_COMMON,LOG_ERR,"Couldn't open directory %s",path);
+ ABORT(R_FAILED);
+ }
+
+ while(p=fts_read(fts)){
+ switch(p->fts_info){
+ case FTS_D:
+ break;
+ case FTS_DOT:
+ break;
+ case FTS_ERR:
+ r_log_e(LOG_COMMON,LOG_ERR,"Problem reading %s",p->fts_path);
+ break;
+ default:
+ r_log(LOG_COMMON,LOG_DEBUG,"Removing %s",p->fts_path);
+ errno=0;
+ if(remove(p->fts_path)){
+ r_log_e(LOG_COMMON,LOG_ERR,"Problem removing %s",p->fts_path);
+ failed=1;
+ }
+ }
+ }
+
+ if(failed)
+ ABORT(R_FAILED);
+
+ _status=0;
+ abort:
+ if(fts) fts_close(fts);
+ return(_status);
+ }
+#endif
+
+int nr_write_pid_file(char *pid_filename)
+ {
+ FILE *fp;
+ int _status;
+
+ if(!pid_filename)
+ ABORT(R_BAD_ARGS);
+
+ unlink(pid_filename);
+
+ if(!(fp=fopen(pid_filename,"w"))){
+ r_log(LOG_GENERIC,LOG_CRIT,"Couldn't open PID file: %s",strerror(errno));
+ ABORT(R_NOT_FOUND);
+ }
+
+ fprintf(fp,"%d\n",getpid());
+
+ fclose(fp);
+
+ chmod(pid_filename,S_IRUSR | S_IRGRP | S_IROTH);
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+#endif
+
+int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val)
+ {
+ int r,_status;
+ UINT4 my_val;
+
+ if(r=NR_reg_get_uint4(key,&my_val)){
+ r_log(log_fac,LOG_ERR,"Couldn't get key '%s', error %d",key,r);
+ ABORT(r);
+ }
+
+ if((min>0) && (my_val<min)){
+ r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%lu, (min = %lu)",key,(unsigned long)my_val,(unsigned long)min);
+ ABORT(R_BAD_DATA);
+ }
+
+ if(my_val>max){
+ r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%lu, (max = %lu)",key,(unsigned long)my_val,(unsigned long)max);
+ ABORT(R_BAD_DATA);
+ }
+
+ *val=my_val;
+ _status=0;
+
+ abort:
+ if(die && _status){
+ r_log(log_fac,LOG_CRIT,"Exiting due to invalid configuration (key '%s')",key);
+ exit(1);
+ }
+ return(_status);
+ }
+
+int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val)
+ {
+ int r,_status;
+ UINT8 my_val;
+
+ if(r=NR_reg_get_uint8(key,&my_val)){
+ r_log(log_fac,LOG_ERR,"Couldn't get key '%s', error %d",key,r);
+ ABORT(r);
+ }
+
+ if(my_val<min){
+ r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%llu, (min = %llu)",key,my_val,min);
+ ABORT(R_BAD_DATA);
+ }
+
+ if(my_val>max){
+ r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%llu, (max = %llu)",key,my_val,max);
+ ABORT(R_BAD_DATA);
+ }
+
+ *val=my_val;
+ _status=0;
+
+ abort:
+ if(die && _status){
+ r_log(log_fac,LOG_CRIT,"Exiting due to invalid configuration (key '%s')",key);
+ exit(1);
+ }
+ return(_status);
+ }
+
+#if defined(LINUX) || defined(WIN32)
+/*-
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
+
+#endif /* LINUX or WIN32 */
+
+#if defined(USE_OWN_INET_NTOP) || defined(WIN32)
+#include <errno.h>
+#ifdef WIN32
+#include <Ws2ipdef.h>
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+#else
+#include <sys/socket.h>
+#endif
+#define INET6
+
+/* inet_ntop implementation from NetBSD */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(NS_INADDRSZ)
+# define NS_INADDRSZ 4
+#endif
+#if !defined(NS_IN6ADDRSZ)
+# define NS_IN6ADDRSZ 16
+#endif
+#if !defined(NS_INT16SZ)
+# define NS_INT16SZ 2
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static const char *inet_ntop4(const unsigned char *src, char *dst, size_t size);
+#ifdef INET6
+static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size);
+#endif /* INET6 */
+
+/* char *
+ * inet_ntop(af, src, dst, size)
+ * convert a network format address to presentation format.
+ * return:
+ * pointer to presentation format address (`dst'), or NULL (see errno).
+ * author:
+ * Paul Vixie, 1996.
+ */
+const char *
+inet_ntop(int af, const void *src, char *dst, size_t size)
+{
+
+ switch (af) {
+ case AF_INET:
+ return (inet_ntop4(src, dst, size));
+#ifdef INET6
+ case AF_INET6:
+ return (inet_ntop6(src, dst, size));
+#endif /* INET6 */
+ default:
+ errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+ /* NOTREACHED */
+}
+
+/* const char *
+ * inet_ntop4(src, dst, size)
+ * format an IPv4 address, more or less like inet_ntoa()
+ * return:
+ * `dst' (as a const)
+ * notes:
+ * (1) uses no statics
+ * (2) takes a unsigned char* not an in_addr as input
+ * author:
+ * Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop4(const unsigned char *src, char *dst, size_t size)
+{
+ char tmp[sizeof "255.255.255.255"];
+ int l;
+
+ l = snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
+ src[0], src[1], src[2], src[3]);
+ if (l <= 0 || (size_t) l >= size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ strlcpy(dst, tmp, size);
+ return (dst);
+}
+
+#ifdef INET6
+/* const char *
+ * inet_ntop6(src, dst, size)
+ * convert IPv6 binary address into presentation (printable) format
+ * author:
+ * Paul Vixie, 1996.
+ */
+static const char *
+inet_ntop6(const unsigned char *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
+ char *tp, *ep;
+ struct { int base, len; } best, cur;
+ unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
+ int i;
+ int advance;
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < NS_IN6ADDRSZ; i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ cur.base = -1;
+ best.len = -1; /* XXX gcc */
+ cur.len = -1; /* XXX gcc */
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ ep = tmp + sizeof(tmp);
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base &&
+ i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0) {
+ if (tp + 1 >= ep)
+ return (NULL);
+ *tp++ = ':';
+ }
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 ||
+ (best.len == 7 && words[7] != 0x0001) ||
+ (best.len == 5 && words[5] == 0xffff))) {
+ if (!inet_ntop4(src+12, tp, (size_t)(ep - tp)))
+ return (NULL);
+ tp += strlen(tp);
+ break;
+ }
+ advance = snprintf(tp, (size_t)(ep - tp), "%x", words[i]);
+ if (advance <= 0 || advance >= ep - tp)
+ return (NULL);
+ tp += advance;
+ }
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) ==
+ (NS_IN6ADDRSZ / NS_INT16SZ)) {
+ if (tp + 1 >= ep)
+ return (NULL);
+ *tp++ = ':';
+ }
+ if (tp + 1 >= ep)
+ return (NULL);
+ *tp++ = '\0';
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size) {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ strlcpy(dst, tmp, size);
+ return (dst);
+}
+#endif /* INET6 */
+
+#ifdef WIN32
+/* Not exactly, will forgive stuff like <addr>:<port> */
+int inet_pton(int af, const char *src, void *dst)
+{
+ struct sockaddr_storage ss;
+ int addrlen = sizeof(ss);
+
+ if (af != AF_INET && af != AF_INET6) {
+ return -1;
+ }
+
+ if (!WSAStringToAddressA(src, af, NULL, (struct sockaddr*)&ss, &addrlen)) {
+ if (af == AF_INET) {
+ struct sockaddr_in *in = (struct sockaddr_in*)&ss;
+ memcpy(dst, &in->sin_addr, sizeof(struct in_addr));
+ } else {
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&ss;
+ memcpy(dst, &in6->sin6_addr, sizeof(struct in6_addr));
+ }
+ return 1;
+ }
+ return 0;
+}
+#endif /* WIN32 */
+
+#endif
+
+#ifdef WIN32
+#include <time.h>
+/* this is only millisecond-accurate, but that should be OK */
+
+int gettimeofday(struct timeval *tv, void *tz)
+ {
+ SYSTEMTIME st;
+ FILETIME ft;
+ ULARGE_INTEGER u;
+
+ GetLocalTime (&st);
+
+ /* strangely, the FILETIME is the number of 100 nanosecond (0.1 us) intervals
+ * since the Epoch */
+ SystemTimeToFileTime(&st, &ft);
+ u.HighPart = ft.dwHighDateTime;
+ u.LowPart = ft.dwLowDateTime;
+
+ tv->tv_sec = (long) (u.QuadPart / 10000000L);
+ tv->tv_usec = (long) (st.wMilliseconds * 1000);;
+
+ return 0;
+ }
+
+#if _MSC_VER < 1900
+int snprintf(char *buffer, size_t n, const char *format, ...)
+{
+ va_list argp;
+ int ret;
+ va_start(argp, format);
+ ret = _vscprintf(format, argp);
+ vsnprintf_s(buffer, n, _TRUNCATE, format, argp);
+ va_end(argp);
+ return ret;
+}
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/src/util/util.h b/media/mtransport/third_party/nrappkit/src/util/util.h
new file mode 100644
index 000000000..869b72863
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/src/util/util.h
@@ -0,0 +1,74 @@
+/**
+ util.h
+
+
+ Copyright (C) 2001-2003, Network Resonance, Inc.
+ Copyright (C) 2006, Network Resonance, Inc.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Network Resonance, Inc. nor the name of any
+ contributors to this software may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Wed Dec 26 17:20:23 2001
+ */
+
+
+#ifndef _util_h
+#define _util_h
+
+#include "registry.h"
+
+int nr_get_filename(char *base,char *name, char **namep);
+#if 0
+#include <openssl/ssl.h>
+
+int read_RSA_private_key(char *base, char *name,RSA **keyp);
+#endif
+void nr_errprintf_log(const char *fmt,...);
+void nr_errprintf_log2(void *ignore, const char *fmt,...);
+extern int nr_util_default_log_facility;
+
+int nr_read_data(int fd,char *buf,int len);
+int nr_drop_privileges(char *username);
+int nr_hex_ascii_dump(Data *data);
+int nr_fwrite_all(FILE *fp,UCHAR *buf,int len);
+int nr_sha1_file(char *filename,UCHAR *out);
+int nr_bin2hex(UCHAR *in,int len,UCHAR *out);
+int nr_rm_tree(char *path);
+int nr_write_pid_file(char *pid_filename);
+
+int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val);
+int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val);
+
+#ifdef WIN32
+int snprintf(char *buffer, size_t n, const char *format, ...);
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#endif
+
diff --git a/media/mtransport/third_party/nrappkit/upstream.diff b/media/mtransport/third_party/nrappkit/upstream.diff
new file mode 100644
index 000000000..df296677f
--- /dev/null
+++ b/media/mtransport/third_party/nrappkit/upstream.diff
@@ -0,0 +1,391 @@
+diff --git a/media/mtransport/third_party/nrappkit/src/event/async_wait.h b/media/mtransport/third_party/nrappkit/src/event/async_wait.h
+--- a/media/mtransport/third_party/nrappkit/src/event/async_wait.h
++++ b/media/mtransport/third_party/nrappkit/src/event/async_wait.h
+@@ -38,21 +38,21 @@
+
+
+ #ifndef _async_wait_h
+ #define _async_wait_h
+
+ #ifdef HAVE_SYS_TIME_H
+ #include <sys/time.h>
+ #endif
+ #include <csi_platform.h>
+
+-typedef void (*NR_async_cb)(int resource,int how,void *arg);
++typedef void (*NR_async_cb)(NR_SOCKET resource,int how,void *arg);
+
+ #define NR_ASYNC_WAIT_READ 0
+ #define NR_ASYNC_WAIT_WRITE 1
+
+
+ int NR_async_wait_init(void);
+ int NR_async_wait(NR_SOCKET sock, int how, NR_async_cb cb,void *cb_arg,
+ char *function,int line);
+ int NR_async_cancel(NR_SOCKET sock,int how);
+ int NR_async_schedule(NR_async_cb cb,void *arg,char *function,int line);
+diff --git a/media/mtransport/third_party/nrappkit/src/log/r_log.c b/media/mtransport/third_party/nrappkit/src/log/r_log.c
+--- a/media/mtransport/third_party/nrappkit/src/log/r_log.c
++++ b/media/mtransport/third_party/nrappkit/src/log/r_log.c
+@@ -33,28 +33,41 @@
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ ekr@rtfm.com Mon Dec 3 15:24:38 2001
+ */
+
+
+ static char *RCSSTRING __UNUSED__ ="$Id: r_log.c,v 1.10 2008/11/25 22:25:18 adamcain Exp $";
+
+
++#ifdef LINUX
++#define _BSD_SOURCE
++#endif
++
+ #include "r_log.h"
+ #include "hex.h"
++
+ #include <string.h>
++#include <errno.h>
++#ifndef _MSC_VER
++#include <strings.h>
++#include <syslog.h>
++#endif
+ #include <registry.h>
+ #include <time.h>
++
++
+ #include "nr_common.h"
+ #include "nr_reg_keys.h"
+
++
+ #define LOGGING_DEFAULT_LEVEL 5
+
+ int NR_LOG_LOGGING = 0;
+
+ static char *log_level_strings[]={
+ "EMERG",
+ "ALERT",
+ "CRIT",
+ "ERR",
+ "WARNING",
+diff --git a/media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h b/media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
+--- a/media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
++++ b/media/mtransport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
+@@ -37,17 +37,21 @@
+ */
+
+
+ #ifndef _platform_h
+ #define _platform_h
+
+ #include <unistd.h>
+
+ #define STDIO_BYTES_BUFFERED(fp) (fp->_r)
+
++#ifdef NR_SOCKET_IS_VOID_PTR
++typedef void* NR_SOCKET;
++#else
+ typedef int NR_SOCKET;
+ #define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count))
+ #define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count))
+ #define NR_SOCKET_CLOSE(sock) close(sock)
++#endif
+
+ #endif
+
+diff --git a/media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h b/media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h
+--- a/media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h
++++ b/media/mtransport/third_party/nrappkit/src/port/linux/include/csi_platform.h
+@@ -35,17 +35,21 @@
+
+ ekr@rtfm.com Mon Dec 13 17:26:51 2004
+ */
+
+
+ #ifndef _platform_h
+ #define _platform_h
+
+ #include <linux_funcs.h>
+
++#ifdef NR_SOCKET_IS_VOID_PTR
++typedef void* NR_SOCKET;
++#else
+ typedef int NR_SOCKET;
+ #define NR_SOCKET_READ(sock,buf,count) read((sock),(buf),(count))
+ #define NR_SOCKET_WRITE(sock,buf,count) write((sock),(buf),(count))
+ #define NR_SOCKET_CLOSE(sock) close(sock)
++#endif
+
+ #endif
+
+diff --git a/media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h b/media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h
+--- a/media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h
++++ b/media/mtransport/third_party/nrappkit/src/port/win32/include/csi_platform.h
+@@ -44,45 +44,51 @@
+ #define _WIN32_WINNT 0x0400 // This prevents weird "'TryEnterCriticalSection': identifier not found"
+ // compiler errors when poco/win32_mutex.h is included
+ #endif
+
+ #define UINT8 UBLAH_IGNORE_ME_PLEASE
+ #define INT8 BLAH_IGNORE_ME_PLEASE
+ #include <winsock2.h>
+ #undef UINT8
+ #undef INT8
+ #include <r_types.h>
++#include <errno.h>
+
+ #define strcasecmp _stricmp
+ #define strncasecmp _strnicmp
+-#define snprintf _snprintf
+
+ #define strcasestr stristr
+
+ /* Hack version of strlcpy (in util/util.c) */
+ size_t strlcat(char *dst, const char *src, size_t siz);
+
+ /* Hack version of getopt() (in util/getopt.c) */
+ int getopt(int argc, char *argv[], char *opstring);
+ extern char *optarg;
+ extern int optind;
+ extern int opterr;
+
+ /* Hack version of gettimeofday() (in util/util.c) */
+ int gettimeofday(struct timeval *tv, void *tz);
+
++#ifdef NR_SOCKET_IS_VOID_PTR
++typedef void* NR_SOCKET;
++#else
+ typedef SOCKET NR_SOCKET;
+ #define NR_SOCKET_READ(sock,buf,count) recv((sock),(buf),(count),0)
+ #define NR_SOCKET_WRITE(sock,buf,count) send((sock),(buf),(count),0)
+ #define NR_SOCKET_CLOSE(sock) closesocket(sock)
++#endif
+
++#ifndef EHOSTUNREACH
+ #define EHOSTUNREACH WSAEHOSTUNREACH
++#endif
+
+ #define LOG_EMERG 0
+ #define LOG_ALERT 1
+ #define LOG_CRIT 2
+ #define LOG_ERR 3
+ #define LOG_WARNING 4
+ #define LOG_NOTICE 5
+ #define LOG_INFO 6
+ #define LOG_DEBUG 7
+
+diff --git a/media/mtransport/third_party/nrappkit/src/registry/registry.c b/media/mtransport/third_party/nrappkit/src/registry/registry.c
+--- a/media/mtransport/third_party/nrappkit/src/registry/registry.c
++++ b/media/mtransport/third_party/nrappkit/src/registry/registry.c
+@@ -37,21 +37,22 @@
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ */
+
+ #include <assert.h>
+ #include <string.h>
+-#ifndef WIN32
++#ifndef _MSC_VER
++#include <strings.h>
+ #include <sys/param.h>
+ #include <netinet/in.h>
+ #endif
+ #ifdef OPENSSL
+ #include <openssl/ssl.h>
+ #endif
+ #include <ctype.h>
+ #include "registry.h"
+ #include "registry_int.h"
+ #include "registry_vtbl.h"
+diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c b/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c
+--- a/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c
++++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_crc32.c
+@@ -58,20 +58,21 @@
+ * the crc computation is finished. The crc should be complemented
+ * before transmission.
+ * The variable corresponding to the macro argument "crc" should
+ * be an unsigned long and should be preset to all ones for Ethernet
+ * use. An error-free packet will leave 0xDEBB20E3 in the crc.
+ * Spencer Garrett <srg@quick.com>
+ */
+
+
+ #include <sys/types.h>
++#include <r_types.h>
+
+ static char *RCSSTRING __UNUSED__ ="$Id: r_crc32.c,v 1.4 2008/11/26 03:22:02 adamcain Exp $";
+
+ #ifdef WIN32
+ #define u_int32_t UINT4
+ #endif
+
+ #define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+ /* generated using the AUTODIN II polynomial
+diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h
+--- a/media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h
++++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_macros.h
+@@ -118,20 +118,20 @@
+ #ifndef MIN
+ #define MIN(a,b) ((a)>(b))?(b):(a)
+ #endif
+
+ #ifndef MAX
+ #define MAX(a,b) ((b)>(a))?(b):(a)
+ #endif
+
+ #ifdef DEBUG
+ #define DBG(a) debug a
+-int debug(int class,char *format,...);
++int debug(int cls, char *format,...);
+ #else
+ #define DBG(a)
+ #endif
+
+ #define UNIMPLEMENTED do { fprintf(stderr,"%s:%d Function %s unimplemented\n",__FILE__,__LINE__,__FUNCTION__); abort(); } while(0)
+
+ #include "r_memory.h"
+
+ #endif
+diff --git a/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h b/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h
+--- a/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h
++++ b/media/mtransport/third_party/nrappkit/src/util/libekr/r_time.h
+@@ -77,35 +77,25 @@
+ $Id: r_time.h,v 1.4 2007/06/26 22:37:57 adamcain Exp $
+
+
+ ekr@rtfm.com Thu Mar 4 08:45:41 1999
+ */
+
+
+ #ifndef _r_time_h
+ #define _r_time_h
+
++#include <csi_platform.h>
+
+-#ifdef WIN32
+-# include <time.h>
+-#else
+-/* Cribbed from the autoconf doc */
+-# if TIME_WITH_SYS_TIME
+-# include <sys/time.h>
+-# include <time.h>
+-# else
+-# if HAVE_SYS_TIME_H
+-# include <sys/time.h>
+-# else
+-# include <time.h>
+-# endif
+-# endif
++#ifndef WIN32
++#include <sys/time.h>
++#include <time.h>
+ #endif
+
+ int r_timeval_diff(struct timeval *t1,struct timeval *t0, struct timeval *diff);
+ int r_timeval_add(struct timeval *t1,struct timeval *t2, struct timeval *sum);
+ int r_timeval_cmp(struct timeval *t1,struct timeval *t2);
+
+ UINT8 r_timeval2int(struct timeval *tv);
+ int r_int2timeval(UINT8 t,struct timeval *tv);
+ UINT8 r_gettimeint(void);
+
+diff --git a/media/mtransport/third_party/nrappkit/src/util/util.c b/media/mtransport/third_party/nrappkit/src/util/util.c
+--- a/media/mtransport/third_party/nrappkit/src/util/util.c
++++ b/media/mtransport/third_party/nrappkit/src/util/util.c
+@@ -292,20 +292,22 @@ int nr_sha1_file(char *filename,UCHAR *o
+
+ return(_status);
+ }
+
+ #endif
+
+ #ifdef WIN32
+ // TODO
+ #else
+
++#if 0
++
+ #include <fts.h>
+
+ int nr_rm_tree(char *path)
+ {
+ FTS *fts=0;
+ FTSENT *p;
+ int failed=0;
+ int _status;
+ char *argv[2];
+
+@@ -337,20 +339,21 @@ int nr_rm_tree(char *path)
+ }
+
+ if(failed)
+ ABORT(R_FAILED);
+
+ _status=0;
+ abort:
+ if(fts) fts_close(fts);
+ return(_status);
+ }
++#endif
+
+ int nr_write_pid_file(char *pid_filename)
+ {
+ FILE *fp;
+ int _status;
+
+ if(!pid_filename)
+ ABORT(R_BAD_ARGS);
+
+ unlink(pid_filename);
+@@ -739,12 +742,24 @@ int gettimeofday(struct timeval *tv, voi
+ * since the Epoch */
+ SystemTimeToFileTime(&st, &ft);
+ u.HighPart = ft.dwHighDateTime;
+ u.LowPart = ft.dwLowDateTime;
+
+ tv->tv_sec = (long) (u.QuadPart / 10000000L);
+ tv->tv_usec = (long) (st.wMilliseconds * 1000);;
+
+ return 0;
+ }
++
++int snprintf(char *buffer, size_t n, const char *format, ...)
++{
++ va_list argp;
++ int ret;
++ va_start(argp, format);
++ ret = _vscprintf(format, argp);
++ vsnprintf_s(buffer, n, _TRUNCATE, format, argp);
++ va_end(argp);
++ return ret;
++}
++
+ #endif
+
+diff --git a/media/mtransport/third_party/nrappkit/src/util/util.h b/media/mtransport/third_party/nrappkit/src/util/util.h
+--- a/media/mtransport/third_party/nrappkit/src/util/util.h
++++ b/media/mtransport/third_party/nrappkit/src/util/util.h
+@@ -57,12 +57,17 @@ int nr_drop_privileges(char *username);
+ int nr_hex_ascii_dump(Data *data);
+ int nr_fwrite_all(FILE *fp,UCHAR *buf,int len);
+ int nr_sha1_file(char *filename,UCHAR *out);
+ int nr_bin2hex(UCHAR *in,int len,UCHAR *out);
+ int nr_rm_tree(char *path);
+ int nr_write_pid_file(char *pid_filename);
+
+ int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val);
+ int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val);
+
++#ifdef WIN32
++int snprintf(char *buffer, size_t n, const char *format, ...);
++const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+ #endif
+
++#endif
++
diff --git a/media/mtransport/transportflow.cpp b/media/mtransport/transportflow.cpp
new file mode 100644
index 000000000..6f9ba6c42
--- /dev/null
+++ b/media/mtransport/transportflow.cpp
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+#include <deque>
+
+#include "logging.h"
+#include "runnable_utils.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+NS_IMPL_ISUPPORTS0(TransportFlow)
+
+// There are some hacks here to allow destruction off of
+// the main thread.
+TransportFlow::~TransportFlow() {
+ // Make sure that if we are off the right thread, we have
+ // no more attached signals.
+ if (!CheckThreadInt()) {
+ MOZ_ASSERT(SignalStateChange.is_empty());
+ MOZ_ASSERT(SignalPacketReceived.is_empty());
+ }
+
+ // Push the destruction onto the STS thread. Note that there
+ // is still some possibility that someone is accessing this
+ // object simultaneously, but as long as smart pointer discipline
+ // is maintained, it shouldn't be possible to access and
+ // destroy it simultaneously. The conversion to an nsAutoPtr
+ // ensures automatic destruction of the queue at exit of
+ // DestroyFinal.
+ nsAutoPtr<std::deque<TransportLayer*>> layers_tmp(layers_.release());
+ RUN_ON_THREAD(target_,
+ WrapRunnableNM(&TransportFlow::DestroyFinal, layers_tmp),
+ NS_DISPATCH_NORMAL);
+}
+
+void TransportFlow::DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers) {
+ ClearLayers(layers.get());
+}
+
+void TransportFlow::ClearLayers(std::queue<TransportLayer *>* layers) {
+ while (!layers->empty()) {
+ delete layers->front();
+ layers->pop();
+ }
+}
+
+void TransportFlow::ClearLayers(std::deque<TransportLayer *>* layers) {
+ while (!layers->empty()) {
+ delete layers->front();
+ layers->pop_front();
+ }
+}
+
+nsresult TransportFlow::PushLayer(TransportLayer *layer) {
+ CheckThread();
+ UniquePtr<TransportLayer> layer_tmp(layer); // Destroy on failure.
+
+ // Don't allow pushes once we are in error state.
+ if (state_ == TransportLayer::TS_ERROR) {
+ MOZ_MTLOG(ML_ERROR, id_ + ": Can't call PushLayer in error state for flow");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = layer->Init();
+ if (!NS_SUCCEEDED(rv)) {
+ // Destroy the rest of the flow, because it's no longer in an acceptable
+ // state.
+ ClearLayers(layers_.get());
+
+ // Set ourselves to have failed.
+ MOZ_MTLOG(ML_ERROR, id_ << ": Layer initialization failed; invalidating");
+ StateChangeInt(TransportLayer::TS_ERROR);
+
+ return rv;
+ }
+ EnsureSameThread(layer);
+
+ TransportLayer *old_layer = layers_->empty() ? nullptr : layers_->front();
+
+ // Re-target my signals to the new layer
+ if (old_layer) {
+ old_layer->SignalStateChange.disconnect(this);
+ old_layer->SignalPacketReceived.disconnect(this);
+ }
+ layers_->push_front(layer_tmp.release());
+ layer->Inserted(this, old_layer);
+
+ layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
+ layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
+ StateChangeInt(layer->state());
+
+ return NS_OK;
+}
+
+// This is all-or-nothing.
+nsresult TransportFlow::PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers) {
+ CheckThread();
+
+ MOZ_ASSERT(!layers->empty());
+ if (layers->empty()) {
+ MOZ_MTLOG(ML_ERROR, id_ << ": Can't call PushLayers with empty layers");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Don't allow pushes once we are in error state.
+ if (state_ == TransportLayer::TS_ERROR) {
+ MOZ_MTLOG(ML_ERROR,
+ id_ << ": Can't call PushLayers in error state for flow ");
+ ClearLayers(layers.get());
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = NS_OK;
+
+ // Disconnect all the old signals.
+ disconnect_all();
+
+ TransportLayer *layer;
+
+ while (!layers->empty()) {
+ TransportLayer *old_layer = layers_->empty() ? nullptr : layers_->front();
+ layer = layers->front();
+
+ rv = layer->Init();
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_ERROR,
+ id_ << ": Layer initialization failed; invalidating flow ");
+ break;
+ }
+
+ EnsureSameThread(layer);
+
+ // Push the layer onto the queue.
+ layers_->push_front(layer);
+ layers->pop();
+ layer->Inserted(this, old_layer);
+ }
+
+ if (NS_FAILED(rv)) {
+ // Destroy any layers we could not push.
+ ClearLayers(layers.get());
+
+ // Now destroy the rest of the flow, because it's no longer
+ // in an acceptable state.
+ ClearLayers(layers_.get());
+
+ // Set ourselves to have failed.
+ StateChangeInt(TransportLayer::TS_ERROR);
+
+ // Return failure.
+ return rv;
+ }
+
+ // Finally, attach ourselves to the top layer.
+ layer->SignalStateChange.connect(this, &TransportFlow::StateChange);
+ layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived);
+ StateChangeInt(layer->state()); // Signals if the state changes.
+
+ return NS_OK;
+}
+
+TransportLayer *TransportFlow::top() const {
+ CheckThread();
+
+ return layers_->empty() ? nullptr : layers_->front();
+}
+
+TransportLayer *TransportFlow::GetLayer(const std::string& id) const {
+ CheckThread();
+
+ for (std::deque<TransportLayer *>::const_iterator it = layers_->begin();
+ it != layers_->end(); ++it) {
+ if ((*it)->id() == id)
+ return *it;
+ }
+
+ return nullptr;
+}
+
+TransportLayer::State TransportFlow::state() {
+ CheckThread();
+
+ return state_;
+}
+
+TransportResult TransportFlow::SendPacket(const unsigned char *data,
+ size_t len) {
+ CheckThread();
+
+ if (state_ != TransportLayer::TS_OPEN) {
+ return TE_ERROR;
+ }
+ return top() ? top()->SendPacket(data, len) : TE_ERROR;
+}
+
+bool TransportFlow::Contains(TransportLayer *layer) const {
+ if (layers_) {
+ for (auto l = layers_->begin(); l != layers_->end(); ++l) {
+ if (*l == layer) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TransportFlow::EnsureSameThread(TransportLayer *layer) {
+ // Enforce that if any of the layers have a thread binding,
+ // they all have the same binding.
+ if (target_) {
+ const nsCOMPtr<nsIEventTarget>& lthread = layer->GetThread();
+
+ if (lthread && (lthread != target_))
+ MOZ_CRASH();
+ }
+ else {
+ target_ = layer->GetThread();
+ }
+}
+
+void TransportFlow::StateChangeInt(TransportLayer::State state) {
+ CheckThread();
+
+ if (state == state_) {
+ return;
+ }
+
+ state_ = state;
+ SignalStateChange(this, state_);
+}
+
+void TransportFlow::StateChange(TransportLayer *layer,
+ TransportLayer::State state) {
+ CheckThread();
+
+ StateChangeInt(state);
+}
+
+void TransportFlow::PacketReceived(TransportLayer* layer,
+ const unsigned char *data,
+ size_t len) {
+ CheckThread();
+
+ SignalPacketReceived(this, data, len);
+}
+
+} // close namespace
diff --git a/media/mtransport/transportflow.h b/media/mtransport/transportflow.h
new file mode 100644
index 000000000..b21764046
--- /dev/null
+++ b/media/mtransport/transportflow.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef transportflow_h__
+#define transportflow_h__
+
+#include <deque>
+#include <queue>
+#include <string>
+
+#include "nscore.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/UniquePtr.h"
+#include "transportlayer.h"
+#include "m_cpp_utils.h"
+#include "nsAutoPtr.h"
+
+// A stack of transport layers acts as a flow.
+// Generally, one reads and writes to the top layer.
+
+// This code has a confusing hybrid threading model which
+// probably needs some eventual refactoring.
+// TODO(ekr@rtfm.com): Bug 844891
+//
+// TransportFlows are not inherently bound to a thread *but*
+// TransportLayers can be. If any layer in a flow is bound
+// to a given thread, then all layers in the flow MUST be
+// bound to that thread and you can only manipulate the
+// flow (push layers, write, etc.) on that thread.
+//
+// The sole official exception to this is that you are
+// allowed to *destroy* a flow off the bound thread provided
+// that there are no listeners on its signals. This exception
+// is designed to allow idioms where you create the flow
+// and then something goes wrong and you destroy it and
+// you don't want to bother with a thread dispatch.
+//
+// Eventually we hope to relax the "no listeners"
+// restriction by thread-locking the signals, but previous
+// attempts have caused deadlocks.
+//
+// Most of these invariants are enforced by hard asserts
+// (i.e., those which fire even in production builds).
+
+namespace mozilla {
+
+class TransportFlow final : public nsISupports,
+ public sigslot::has_slots<> {
+ public:
+ TransportFlow()
+ : id_("(anonymous)"),
+ state_(TransportLayer::TS_NONE),
+ layers_(new std::deque<TransportLayer *>) {}
+ explicit TransportFlow(const std::string id)
+ : id_(id),
+ state_(TransportLayer::TS_NONE),
+ layers_(new std::deque<TransportLayer *>) {}
+
+ const std::string& id() const { return id_; }
+
+ // Layer management. Note PushLayer() is not thread protected, so
+ // either:
+ // (a) Do it in the thread handling the I/O
+ // (b) Do it before you activate the I/O system
+ //
+ // The flow takes ownership of the layers after a successful
+ // push.
+ nsresult PushLayer(TransportLayer *layer);
+
+ // Convenience function to push multiple layers on. Layers
+ // are pushed on in the order that they are in the queue.
+ // Any failures cause the flow to become inoperable and
+ // destroys all the layers including those already pushed.
+ // TODO(ekr@rtfm.com): Change layers to be ref-counted.
+ nsresult PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers);
+
+ TransportLayer *top() const;
+ TransportLayer *GetLayer(const std::string& id) const;
+
+ // Wrappers for whatever TLayer happens to be the top layer
+ // at the time. This way you don't need to do top()->Foo().
+ TransportLayer::State state(); // Current state
+ TransportResult SendPacket(const unsigned char *data, size_t len);
+
+ // State has changed. Reflects the top flow.
+ sigslot::signal2<TransportFlow *, TransportLayer::State>
+ SignalStateChange;
+
+ // Data received on the flow
+ sigslot::signal3<TransportFlow*, const unsigned char *, size_t>
+ SignalPacketReceived;
+
+ bool Contains(TransportLayer *layer) const;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ private:
+ ~TransportFlow();
+
+ DISALLOW_COPY_ASSIGN(TransportFlow);
+
+ // Check if we are on the right thread
+ void CheckThread() const {
+ if (!CheckThreadInt())
+ MOZ_CRASH();
+ }
+
+ bool CheckThreadInt() const {
+ bool on;
+
+ if (!target_) // OK if no thread set.
+ return true;
+ if (NS_FAILED(target_->IsOnCurrentThread(&on)))
+ return false;
+
+ return on;
+ }
+
+ void EnsureSameThread(TransportLayer *layer);
+
+ void StateChange(TransportLayer *layer, TransportLayer::State state);
+ void StateChangeInt(TransportLayer::State state);
+ void PacketReceived(TransportLayer* layer, const unsigned char *data,
+ size_t len);
+ static void DestroyFinal(nsAutoPtr<std::deque<TransportLayer *> > layers);
+
+ // Overload needed because we use deque internally and queue externally.
+ static void ClearLayers(std::deque<TransportLayer *>* layers);
+ static void ClearLayers(std::queue<TransportLayer *>* layers);
+
+ std::string id_;
+ TransportLayer::State state_;
+ UniquePtr<std::deque<TransportLayer *>> layers_;
+ nsCOMPtr<nsIEventTarget> target_;
+};
+
+} // close namespace
+#endif
diff --git a/media/mtransport/transportlayer.cpp b/media/mtransport/transportlayer.cpp
new file mode 100644
index 000000000..5f05b1d5d
--- /dev/null
+++ b/media/mtransport/transportlayer.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+#include "logging.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+#include "nsThreadUtils.h"
+
+// Logging context
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+nsresult TransportLayer::Init() {
+ if (state_ != TS_NONE)
+ return state_ == TS_ERROR ? NS_ERROR_FAILURE : NS_OK;
+
+ nsresult rv = InitInternal();
+
+ if (!NS_SUCCEEDED(rv)) {
+ state_ = TS_ERROR;
+ return rv;
+ }
+ state_ = TS_INIT;
+
+ return NS_OK;
+}
+
+void TransportLayer::Inserted(TransportFlow *flow, TransportLayer *downward) {
+ downward_ = downward;
+ flow_id_ = flow->id();
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Inserted: downward='" <<
+ (downward ? downward->id(): "none") << "'");
+
+ WasInserted();
+}
+
+void TransportLayer::SetState(State state, const char *file, unsigned line) {
+ if (state != state_) {
+ MOZ_MTLOG(state == TS_ERROR ? ML_ERROR : ML_DEBUG,
+ file << ":" << line << ": " <<
+ LAYER_INFO << "state " << state_ << "->" << state);
+ state_ = state;
+ SignalStateChange(this, state);
+ }
+}
+
+} // close namespace
diff --git a/media/mtransport/transportlayer.h b/media/mtransport/transportlayer.h
new file mode 100644
index 000000000..fb40904c3
--- /dev/null
+++ b/media/mtransport/transportlayer.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef transportlayer_h__
+#define transportlayer_h__
+
+#include "sigslot.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+
+#include "m_cpp_utils.h"
+
+namespace mozilla {
+
+class TransportFlow;
+
+typedef int TransportResult;
+
+enum {
+ TE_WOULDBLOCK = -1, TE_ERROR = -2, TE_INTERNAL = -3
+};
+
+#define TRANSPORT_LAYER_ID(name) \
+ virtual const std::string id() const { return name; } \
+ static std::string ID() { return name; }
+
+// Abstract base class for network transport layers.
+class TransportLayer : public sigslot::has_slots<> {
+ public:
+ // The state of the transport flow
+ // We can't use "ERROR" because Windows has a macro named "ERROR"
+ enum State { TS_NONE, TS_INIT, TS_CONNECTING, TS_OPEN, TS_CLOSED, TS_ERROR };
+
+ // Is this a stream or datagram flow
+ TransportLayer() :
+ state_(TS_NONE),
+ flow_id_(),
+ downward_(nullptr) {}
+
+ virtual ~TransportLayer() {}
+
+ // Called to initialize
+ nsresult Init(); // Called by Insert() to set up -- do not override
+ virtual nsresult InitInternal() { return NS_OK; } // Called by Init
+
+ // Called when inserted into a flow
+ virtual void Inserted(TransportFlow *flow, TransportLayer *downward);
+
+ // Downward interface
+ TransportLayer *downward() { return downward_; }
+
+ // Get the state
+ State state() const { return state_; }
+ // Must be implemented by derived classes
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len) = 0;
+
+ // Get the thread.
+ const nsCOMPtr<nsIEventTarget> GetThread() const {
+ return target_;
+ }
+
+ // Event definitions that one can register for
+ // State has changed
+ sigslot::signal2<TransportLayer*, State> SignalStateChange;
+ // Data received on the flow
+ sigslot::signal3<TransportLayer*, const unsigned char *, size_t>
+ SignalPacketReceived;
+
+ // Return the layer id for this layer
+ virtual const std::string id() const = 0;
+
+ // The id of the flow
+ const std::string& flow_id() const {
+ return flow_id_;
+ }
+
+ protected:
+ virtual void WasInserted() {}
+ virtual void SetState(State state, const char *file, unsigned line);
+ // Check if we are on the right thread
+ void CheckThread() const {
+ MOZ_ASSERT(CheckThreadInt(), "Wrong thread");
+ }
+
+ State state_;
+ std::string flow_id_;
+ TransportLayer *downward_; // The next layer in the stack
+ nsCOMPtr<nsIEventTarget> target_;
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayer);
+
+ bool CheckThreadInt() const {
+ bool on;
+
+ if (!target_) // OK if no thread set.
+ return true;
+
+ NS_ENSURE_SUCCESS(target_->IsOnCurrentThread(&on), false);
+ NS_ENSURE_TRUE(on, false);
+
+ return true;
+ }
+};
+
+#define LAYER_INFO "Flow[" << flow_id() << "(none)" << "]; Layer[" << id() << "]: "
+#define TL_SET_STATE(x) SetState((x), __FILE__, __LINE__)
+
+} // close namespace
+#endif
diff --git a/media/mtransport/transportlayerdtls.cpp b/media/mtransport/transportlayerdtls.cpp
new file mode 100644
index 000000000..55bf53f1b
--- /dev/null
+++ b/media/mtransport/transportlayerdtls.cpp
@@ -0,0 +1,1262 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include "transportlayerdtls.h"
+
+#include <algorithm>
+#include <queue>
+#include <sstream>
+
+#include "dtlsidentity.h"
+#include "keyhi.h"
+#include "logging.h"
+#include "mozilla/Move.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIEventTarget.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+#include "transportflow.h"
+
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+static PRDescIdentity transport_layer_identity = PR_INVALID_IO_LAYER;
+
+// TODO: Implement a mode for this where
+// the channel is not ready until confirmed externally
+// (e.g., after cert check).
+
+#define UNIMPLEMENTED \
+ MOZ_MTLOG(ML_ERROR, \
+ "Call to unimplemented function "<< __FUNCTION__); \
+ MOZ_ASSERT(false); \
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0)
+
+#define MAX_ALPN_LENGTH 255
+
+// We need to adapt the NSPR/libssl model to the TransportFlow model.
+// The former wants pull semantics and TransportFlow wants push.
+//
+// - A TransportLayerDtls assumes it is sitting on top of another
+// TransportLayer, which means that events come in asynchronously.
+// - NSS (libssl) wants to sit on top of a PRFileDesc and poll.
+// - The TransportLayerNSPRAdapter is a PRFileDesc containing a
+// FIFO.
+// - When TransportLayerDtls.PacketReceived() is called, we insert
+// the packets in the FIFO and then do a PR_Recv() on the NSS
+// PRFileDesc, which eventually reads off the FIFO.
+//
+// All of this stuff is assumed to happen solely in a single thread
+// (generally the SocketTransportService thread)
+struct Packet {
+ Packet() : data_(nullptr), len_(0) {}
+
+ void Assign(const void *data, int32_t len) {
+ data_.reset(new uint8_t[len]);
+ memcpy(data_.get(), data, len);
+ len_ = len;
+ }
+
+ UniquePtr<uint8_t[]> data_;
+ int32_t len_;
+};
+
+void TransportLayerNSPRAdapter::PacketReceived(const void *data, int32_t len) {
+ if (enabled_) {
+ input_.push(new Packet());
+ input_.back()->Assign(data, len);
+ }
+}
+
+int32_t TransportLayerNSPRAdapter::Recv(void *buf, int32_t buflen) {
+ if (input_.empty()) {
+ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+ return -1;
+ }
+
+ Packet* front = input_.front();
+ if (buflen < front->len_) {
+ MOZ_ASSERT(false, "Not enough buffer space to receive into");
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return -1;
+ }
+
+ int32_t count = front->len_;
+ memcpy(buf, front->data_.get(), count);
+
+ input_.pop();
+ delete front;
+
+ return count;
+}
+
+int32_t TransportLayerNSPRAdapter::Write(const void *buf, int32_t length) {
+ if (!enabled_) {
+ MOZ_MTLOG(ML_WARNING, "Writing to disabled transport layer");
+ return -1;
+ }
+
+ TransportResult r = output_->SendPacket(
+ static_cast<const unsigned char *>(buf), length);
+ if (r >= 0) {
+ return r;
+ }
+
+ if (r == TE_WOULDBLOCK) {
+ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_ERROR, 0);
+ }
+
+ return -1;
+}
+
+
+// Implementation of NSPR methods
+static PRStatus TransportLayerClose(PRFileDesc *f) {
+ f->dtor(f);
+ return PR_SUCCESS;
+}
+
+static int32_t TransportLayerRead(PRFileDesc *f, void *buf, int32_t length) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static int32_t TransportLayerWrite(PRFileDesc *f, const void *buf, int32_t length) {
+ TransportLayerNSPRAdapter *io = reinterpret_cast<TransportLayerNSPRAdapter *>(f->secret);
+ return io->Write(buf, length);
+}
+
+static int32_t TransportLayerAvailable(PRFileDesc *f) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+int64_t TransportLayerAvailable64(PRFileDesc *f) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static PRStatus TransportLayerSync(PRFileDesc *f) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static int32_t TransportLayerSeek(PRFileDesc *f, int32_t offset,
+ PRSeekWhence how) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static int64_t TransportLayerSeek64(PRFileDesc *f, int64_t offset,
+ PRSeekWhence how) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static PRStatus TransportLayerFileInfo(PRFileDesc *f, PRFileInfo *info) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static PRStatus TransportLayerFileInfo64(PRFileDesc *f, PRFileInfo64 *info) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static int32_t TransportLayerWritev(PRFileDesc *f, const PRIOVec *iov,
+ int32_t iov_size, PRIntervalTime to) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static PRStatus TransportLayerConnect(PRFileDesc *f, const PRNetAddr *addr,
+ PRIntervalTime to) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static PRFileDesc *TransportLayerAccept(PRFileDesc *sd, PRNetAddr *addr,
+ PRIntervalTime to) {
+ UNIMPLEMENTED;
+ return nullptr;
+}
+
+static PRStatus TransportLayerBind(PRFileDesc *f, const PRNetAddr *addr) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static PRStatus TransportLayerListen(PRFileDesc *f, int32_t depth) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static PRStatus TransportLayerShutdown(PRFileDesc *f, int32_t how) {
+ // This is only called from NSS when we are the server and the client refuses
+ // to provide a certificate. In this case, the handshake is destined for
+ // failure, so we will just let this pass.
+ TransportLayerNSPRAdapter *io = reinterpret_cast<TransportLayerNSPRAdapter *>(f->secret);
+ io->SetEnabled(false);
+ return PR_SUCCESS;
+}
+
+// This function does not support peek, or waiting until `to`
+static int32_t TransportLayerRecv(PRFileDesc *f, void *buf, int32_t buflen,
+ int32_t flags, PRIntervalTime to) {
+ MOZ_ASSERT(flags == 0);
+ if (flags != 0) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return -1;
+ }
+
+ TransportLayerNSPRAdapter *io = reinterpret_cast<TransportLayerNSPRAdapter *>(f->secret);
+ return io->Recv(buf, buflen);
+}
+
+// Note: this is always nonblocking and assumes a zero timeout.
+static int32_t TransportLayerSend(PRFileDesc *f, const void *buf, int32_t amount,
+ int32_t flags, PRIntervalTime to) {
+ int32_t written = TransportLayerWrite(f, buf, amount);
+ return written;
+}
+
+static int32_t TransportLayerRecvfrom(PRFileDesc *f, void *buf, int32_t amount,
+ int32_t flags, PRNetAddr *addr, PRIntervalTime to) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static int32_t TransportLayerSendto(PRFileDesc *f, const void *buf, int32_t amount,
+ int32_t flags, const PRNetAddr *addr, PRIntervalTime to) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static int16_t TransportLayerPoll(PRFileDesc *f, int16_t in_flags, int16_t *out_flags) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static int32_t TransportLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd,
+ PRNetAddr **raddr,
+ void *buf, int32_t amount, PRIntervalTime t) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static int32_t TransportLayerTransmitFile(PRFileDesc *sd, PRFileDesc *f,
+ const void *headers, int32_t hlen,
+ PRTransmitFileFlags flags, PRIntervalTime t) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static PRStatus TransportLayerGetpeername(PRFileDesc *f, PRNetAddr *addr) {
+ // TODO: Modify to return unique names for each channel
+ // somehow, as opposed to always the same static address. The current
+ // implementation messes up the session cache, which is why it's off
+ // elsewhere
+ addr->inet.family = PR_AF_INET;
+ addr->inet.port = 0;
+ addr->inet.ip = 0;
+
+ return PR_SUCCESS;
+}
+
+static PRStatus TransportLayerGetsockname(PRFileDesc *f, PRNetAddr *addr) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static PRStatus TransportLayerGetsockoption(PRFileDesc *f, PRSocketOptionData *opt) {
+ switch (opt->option) {
+ case PR_SockOpt_Nonblocking:
+ opt->value.non_blocking = PR_TRUE;
+ return PR_SUCCESS;
+ default:
+ UNIMPLEMENTED;
+ break;
+ }
+
+ return PR_FAILURE;
+}
+
+// Imitate setting socket options. These are mostly noops.
+static PRStatus TransportLayerSetsockoption(PRFileDesc *f,
+ const PRSocketOptionData *opt) {
+ switch (opt->option) {
+ case PR_SockOpt_Nonblocking:
+ return PR_SUCCESS;
+ case PR_SockOpt_NoDelay:
+ return PR_SUCCESS;
+ default:
+ UNIMPLEMENTED;
+ break;
+ }
+
+ return PR_FAILURE;
+}
+
+static int32_t TransportLayerSendfile(PRFileDesc *out, PRSendFileData *in,
+ PRTransmitFileFlags flags, PRIntervalTime to) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static PRStatus TransportLayerConnectContinue(PRFileDesc *f, int16_t flags) {
+ UNIMPLEMENTED;
+ return PR_FAILURE;
+}
+
+static int32_t TransportLayerReserved(PRFileDesc *f) {
+ UNIMPLEMENTED;
+ return -1;
+}
+
+static const struct PRIOMethods TransportLayerMethods = {
+ PR_DESC_LAYERED,
+ TransportLayerClose,
+ TransportLayerRead,
+ TransportLayerWrite,
+ TransportLayerAvailable,
+ TransportLayerAvailable64,
+ TransportLayerSync,
+ TransportLayerSeek,
+ TransportLayerSeek64,
+ TransportLayerFileInfo,
+ TransportLayerFileInfo64,
+ TransportLayerWritev,
+ TransportLayerConnect,
+ TransportLayerAccept,
+ TransportLayerBind,
+ TransportLayerListen,
+ TransportLayerShutdown,
+ TransportLayerRecv,
+ TransportLayerSend,
+ TransportLayerRecvfrom,
+ TransportLayerSendto,
+ TransportLayerPoll,
+ TransportLayerAcceptRead,
+ TransportLayerTransmitFile,
+ TransportLayerGetsockname,
+ TransportLayerGetpeername,
+ TransportLayerReserved,
+ TransportLayerReserved,
+ TransportLayerGetsockoption,
+ TransportLayerSetsockoption,
+ TransportLayerSendfile,
+ TransportLayerConnectContinue,
+ TransportLayerReserved,
+ TransportLayerReserved,
+ TransportLayerReserved,
+ TransportLayerReserved
+};
+
+TransportLayerDtls::~TransportLayerDtls() {
+ nspr_io_adapter_->SetEnabled(false);
+ if (timer_) {
+ timer_->Cancel();
+ }
+}
+
+nsresult TransportLayerDtls::InitInternal() {
+ // Get the transport service as an event target
+ nsresult rv;
+ target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get socket transport service");
+ return rv;
+ }
+
+ timer_ = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get timer");
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+
+void TransportLayerDtls::WasInserted() {
+ // Connect to the lower layers
+ if (!Setup()) {
+ TL_SET_STATE(TS_ERROR);
+ }
+}
+
+
+// Set the permitted and default ALPN identifiers.
+// The default is here to allow for peers that don't want to negotiate ALPN
+// in that case, the default string will be reported from GetNegotiatedAlpn().
+// Setting the default to the empty string causes the transport layer to fail
+// if ALPN is not negotiated.
+// Note: we only support Unicode strings here, which are encoded into UTF-8,
+// even though ALPN ostensibly allows arbitrary octet sequences.
+nsresult TransportLayerDtls::SetAlpn(
+ const std::set<std::string>& alpn_allowed,
+ const std::string& alpn_default) {
+
+ alpn_allowed_ = alpn_allowed;
+ alpn_default_ = alpn_default;
+
+ return NS_OK;
+}
+
+
+nsresult TransportLayerDtls::SetVerificationAllowAll() {
+ // Defensive programming
+ if (verification_mode_ != VERIFY_UNSET)
+ return NS_ERROR_ALREADY_INITIALIZED;
+
+ verification_mode_ = VERIFY_ALLOW_ALL;
+
+ return NS_OK;
+}
+
+nsresult
+TransportLayerDtls::SetVerificationDigest(const std::string digest_algorithm,
+ const unsigned char *digest_value,
+ size_t digest_len) {
+ // Defensive programming
+ if (verification_mode_ != VERIFY_UNSET &&
+ verification_mode_ != VERIFY_DIGEST) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ // Note that we do not sanity check these values for length.
+ // We merely ensure they will fit into the buffer.
+ // TODO: is there a Data construct we could use?
+ if (digest_len > kMaxDigestLength)
+ return NS_ERROR_INVALID_ARG;
+
+ digests_.push_back(new VerificationDigest(
+ digest_algorithm, digest_value, digest_len));
+
+ verification_mode_ = VERIFY_DIGEST;
+
+ return NS_OK;
+}
+
+// These are the named groups that we will allow.
+static const SSLNamedGroup NamedGroupPreferences[] = {
+ ssl_grp_ec_curve25519,
+ ssl_grp_ec_secp256r1,
+ ssl_grp_ec_secp384r1,
+ ssl_grp_ffdhe_2048,
+ ssl_grp_ffdhe_3072
+};
+
+// TODO: make sure this is called from STS. Otherwise
+// we have thread safety issues
+bool TransportLayerDtls::Setup() {
+ CheckThread();
+ SECStatus rv;
+
+ if (!downward_) {
+ MOZ_MTLOG(ML_ERROR, "DTLS layer with nothing below. This is useless");
+ return false;
+ }
+ nspr_io_adapter_ = MakeUnique<TransportLayerNSPRAdapter>(downward_);
+
+ if (!identity_) {
+ MOZ_MTLOG(ML_ERROR, "Can't start DTLS without an identity");
+ return false;
+ }
+
+ if (verification_mode_ == VERIFY_UNSET) {
+ MOZ_MTLOG(ML_ERROR,
+ "Can't start DTLS without specifying a verification mode");
+ return false;
+ }
+
+ if (transport_layer_identity == PR_INVALID_IO_LAYER) {
+ transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter");
+ }
+
+ UniquePRFileDesc pr_fd(PR_CreateIOLayerStub(transport_layer_identity,
+ &TransportLayerMethods));
+ MOZ_ASSERT(pr_fd != nullptr);
+ if (!pr_fd)
+ return false;
+ pr_fd->secret = reinterpret_cast<PRFilePrivate *>(nspr_io_adapter_.get());
+
+ UniquePRFileDesc ssl_fd(DTLS_ImportFD(nullptr, pr_fd.get()));
+ MOZ_ASSERT(ssl_fd != nullptr); // This should never happen
+ if (!ssl_fd) {
+ return false;
+ }
+
+ Unused << pr_fd.release(); // ownership transfered to ssl_fd;
+
+ if (role_ == CLIENT) {
+ MOZ_MTLOG(ML_INFO, "Setting up DTLS as client");
+ rv = SSL_GetClientAuthDataHook(ssl_fd.get(), GetClientAuthDataHook,
+ this);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set identity");
+ return false;
+ }
+ } else {
+ MOZ_MTLOG(ML_INFO, "Setting up DTLS as server");
+ // Server side
+ rv = SSL_ConfigSecureServer(ssl_fd.get(), identity_->cert().get(),
+ identity_->privkey(),
+ identity_->auth_type());
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set identity");
+ return false;
+ }
+
+ UniqueCERTCertList zero_certs(CERT_NewCertList());
+ rv = SSL_SetTrustAnchors(ssl_fd.get(), zero_certs.get());
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set trust anchors");
+ return false;
+ }
+
+ // Insist on a certificate from the client
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUEST_CERTIFICATE, PR_TRUE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't request certificate");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_REQUIRE_CERTIFICATE, PR_TRUE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't require certificate");
+ return false;
+ }
+ }
+
+ // Require TLS 1.1 or 1.2. Perhaps some day in the future we will allow TLS
+ // 1.0 for stream modes.
+ SSLVersionRange version_range = {
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_2
+ };
+
+ rv = SSL_VersionRangeSet(ssl_fd.get(), &version_range);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Can't disable SSLv3");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_SESSION_TICKETS, PR_FALSE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable session tickets");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_NO_CACHE, PR_TRUE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable session caching");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_DEFLATE, PR_FALSE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable deflate");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_RENEGOTIATION,
+ SSL_RENEGOTIATE_NEVER);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable renegotiation");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_FALSE_START, PR_FALSE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable false start");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_NO_LOCKS, PR_TRUE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable locks");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable ECDHE key reuse");
+ return false;
+ }
+
+ if (!SetupCipherSuites(ssl_fd)) {
+ return false;
+ }
+
+ rv = SSL_NamedGroupConfig(ssl_fd.get(), NamedGroupPreferences,
+ mozilla::ArrayLength(NamedGroupPreferences));
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set named groups");
+ return false;
+ }
+
+ // Certificate validation
+ rv = SSL_AuthCertificateHook(ssl_fd.get(), AuthCertificateHook,
+ reinterpret_cast<void *>(this));
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set certificate validation hook");
+ return false;
+ }
+
+ if (!SetupAlpn(ssl_fd)) {
+ return false;
+ }
+
+ // Now start the handshake
+ rv = SSL_ResetHandshake(ssl_fd.get(), role_ == SERVER ? PR_TRUE : PR_FALSE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't reset handshake");
+ return false;
+ }
+ ssl_fd_ = Move(ssl_fd);
+
+ // Finally, get ready to receive data
+ downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange);
+ downward_->SignalPacketReceived.connect(this, &TransportLayerDtls::PacketReceived);
+
+ if (downward_->state() == TS_OPEN) {
+ TL_SET_STATE(TS_CONNECTING);
+ Handshake();
+ }
+
+ return true;
+}
+
+bool TransportLayerDtls::SetupAlpn(UniquePRFileDesc& ssl_fd) const {
+ if (alpn_allowed_.empty()) {
+ return true;
+ }
+
+ SECStatus rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_NPN, PR_FALSE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't disable NPN");
+ return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd.get(), SSL_ENABLE_ALPN, PR_TRUE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't enable ALPN");
+ return false;
+ }
+
+ unsigned char buf[MAX_ALPN_LENGTH];
+ size_t offset = 0;
+ for (auto tag = alpn_allowed_.begin();
+ tag != alpn_allowed_.end(); ++tag) {
+ if ((offset + 1 + tag->length()) >= sizeof(buf)) {
+ MOZ_MTLOG(ML_ERROR, "ALPN too long");
+ return false;
+ }
+ buf[offset++] = tag->length();
+ memcpy(buf + offset, tag->c_str(), tag->length());
+ offset += tag->length();
+ }
+ rv = SSL_SetNextProtoNego(ssl_fd.get(), buf, offset);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set ALPN string");
+ return false;
+ }
+ return true;
+}
+
+// Ciphers we need to enable. These are on by default in standard firefox
+// builds, but can be disabled with prefs and they aren't on in our unit tests
+// since that uses NSS default configuration.
+//
+// Only override prefs to comply with MUST statements in the security-arch doc.
+// Anything outside this list is governed by the usual combination of policy
+// and user preferences.
+static const uint32_t EnabledCiphers[] = {
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+};
+
+// Disable all NSS suites modes without PFS or with old and rusty ciphersuites.
+// Anything outside this list is governed by the usual combination of policy
+// and user preferences.
+static const uint32_t DisabledCiphers[] = {
+ // Bug 1310061: disable all SHA384 ciphers until fixed
+ TLS_AES_256_GCM_SHA384,
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
+
+ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ TLS_DHE_DSS_WITH_RC4_128_SHA,
+
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
+ TLS_ECDH_RSA_WITH_RC4_128_SHA,
+
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_128_CBC_SHA256,
+ TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ TLS_RSA_WITH_AES_256_CBC_SHA256,
+ TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
+ TLS_RSA_WITH_SEED_CBC_SHA,
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_MD5,
+
+ TLS_DHE_RSA_WITH_DES_CBC_SHA,
+ TLS_DHE_DSS_WITH_DES_CBC_SHA,
+ TLS_RSA_WITH_DES_CBC_SHA,
+
+ TLS_ECDHE_ECDSA_WITH_NULL_SHA,
+ TLS_ECDHE_RSA_WITH_NULL_SHA,
+ TLS_ECDH_ECDSA_WITH_NULL_SHA,
+ TLS_ECDH_RSA_WITH_NULL_SHA,
+ TLS_RSA_WITH_NULL_SHA,
+ TLS_RSA_WITH_NULL_SHA256,
+ TLS_RSA_WITH_NULL_MD5,
+};
+
+bool TransportLayerDtls::SetupCipherSuites(UniquePRFileDesc& ssl_fd) const {
+ SECStatus rv;
+
+ // Set the SRTP ciphers
+ if (!srtp_ciphers_.empty()) {
+ // Note: std::vector is guaranteed to contiguous
+ rv = SSL_SetSRTPCiphers(ssl_fd.get(), &srtp_ciphers_[0],
+ srtp_ciphers_.size());
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set SRTP cipher suite");
+ return false;
+ }
+ }
+
+ for (const auto& cipher : EnabledCiphers) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Enabling: " << cipher);
+ rv = SSL_CipherPrefSet(ssl_fd.get(), cipher, PR_TRUE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO <<
+ "Unable to enable suite: " << cipher);
+ return false;
+ }
+ }
+
+ for (const auto& cipher : DisabledCiphers) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Disabling: " << cipher);
+
+ PRBool enabled = false;
+ rv = SSL_CipherPrefGet(ssl_fd.get(), cipher, &enabled);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO <<
+ "Unable to check if suite is enabled: " << cipher);
+ return false;
+ }
+ if (enabled) {
+ rv = SSL_CipherPrefSet(ssl_fd.get(), cipher, PR_FALSE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO <<
+ "Unable to disable suite: " << cipher);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+nsresult TransportLayerDtls::GetCipherSuite(uint16_t* cipherSuite) const {
+ CheckThread();
+ if (!cipherSuite) {
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "GetCipherSuite passed a nullptr");
+ return NS_ERROR_NULL_POINTER;
+ }
+ if (state_ != TS_OPEN) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ SSLChannelInfo info;
+ SECStatus rv = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info));
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "GetCipherSuite can't get channel info");
+ return NS_ERROR_FAILURE;
+ }
+ *cipherSuite = info.cipherSuite;
+ return NS_OK;
+}
+
+void TransportLayerDtls::StateChange(TransportLayer *layer, State state) {
+ if (state <= state_) {
+ MOZ_MTLOG(ML_ERROR, "Lower layer state is going backwards from ours");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ switch (state) {
+ case TS_NONE:
+ MOZ_ASSERT(false); // Can't happen
+ break;
+
+ case TS_INIT:
+ MOZ_MTLOG(ML_ERROR,
+ LAYER_INFO << "State change of lower layer to INIT forbidden");
+ TL_SET_STATE(TS_ERROR);
+ break;
+
+ case TS_CONNECTING:
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is connecting.");
+ break;
+
+ case TS_OPEN:
+ MOZ_MTLOG(ML_ERROR,
+ LAYER_INFO << "Lower layer is now open; starting TLS");
+ // Async, since the ICE layer might need to send a STUN response, and we
+ // don't want the handshake to start until that is sent.
+ TL_SET_STATE(TS_CONNECTING);
+ timer_->Cancel();
+ timer_->SetTarget(target_);
+ timer_->InitWithFuncCallback(TimerCallback,
+ this,
+ 0,
+ nsITimer::TYPE_ONE_SHOT);
+ break;
+
+ case TS_CLOSED:
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is now closed");
+ TL_SET_STATE(TS_CLOSED);
+ break;
+
+ case TS_ERROR:
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer experienced an error");
+ TL_SET_STATE(TS_ERROR);
+ break;
+ }
+}
+
+void TransportLayerDtls::Handshake() {
+ // Clear the retransmit timer
+ timer_->Cancel();
+
+ SECStatus rv = SSL_ForceHandshake(ssl_fd_.get());
+
+ if (rv == SECSuccess) {
+ MOZ_MTLOG(ML_NOTICE,
+ LAYER_INFO << "****** SSL handshake completed ******");
+ if (!cert_ok_) {
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Certificate check never occurred");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+ if (!CheckAlpn()) {
+ // Despite connecting, the connection doesn't have a valid ALPN label.
+ // Forcibly close the connection so that the peer isn't left hanging
+ // (assuming the close_notify isn't dropped).
+ ssl_fd_ = nullptr;
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ TL_SET_STATE(TS_OPEN);
+ } else {
+ int32_t err = PR_GetError();
+ switch(err) {
+ case SSL_ERROR_RX_MALFORMED_HANDSHAKE:
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Malformed DTLS message; ignoring");
+ // If this were TLS (and not DTLS), this would be fatal, but
+ // here we're required to ignore bad messages, so fall through
+ MOZ_FALLTHROUGH;
+ case PR_WOULD_BLOCK_ERROR:
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Handshake would have blocked");
+ PRIntervalTime timeout;
+ rv = DTLS_GetHandshakeTimeout(ssl_fd_.get(), &timeout);
+ if (rv == SECSuccess) {
+ uint32_t timeout_ms = PR_IntervalToMilliseconds(timeout);
+
+ MOZ_MTLOG(ML_DEBUG,
+ LAYER_INFO << "Setting DTLS timeout to " << timeout_ms);
+ timer_->SetTarget(target_);
+ timer_->InitWithFuncCallback(TimerCallback,
+ this, timeout_ms,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+ break;
+ default:
+ const char *err_msg = PR_ErrorToName(err);
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "DTLS handshake error " << err << " ("
+ << err_msg << ")");
+ TL_SET_STATE(TS_ERROR);
+ break;
+ }
+ }
+}
+
+// Checks if ALPN was negotiated correctly and returns false if it wasn't.
+// After this returns successfully, alpn_ will be set to the negotiated
+// protocol.
+bool TransportLayerDtls::CheckAlpn() {
+ if (alpn_allowed_.empty()) {
+ return true;
+ }
+
+ SSLNextProtoState alpnState;
+ char chosenAlpn[MAX_ALPN_LENGTH];
+ unsigned int chosenAlpnLen;
+ SECStatus rv = SSL_GetNextProto(ssl_fd_.get(), &alpnState,
+ reinterpret_cast<unsigned char*>(chosenAlpn),
+ &chosenAlpnLen, sizeof(chosenAlpn));
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "ALPN error");
+ return false;
+ }
+ switch (alpnState) {
+ case SSL_NEXT_PROTO_SELECTED:
+ case SSL_NEXT_PROTO_NEGOTIATED:
+ break; // OK
+
+ case SSL_NEXT_PROTO_NO_SUPPORT:
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "ALPN not negotiated, "
+ << (alpn_default_.empty() ? "failing" : "selecting default"));
+ alpn_ = alpn_default_;
+ return !alpn_.empty();
+
+ case SSL_NEXT_PROTO_NO_OVERLAP:
+ // This only happens if there is a custom NPN/ALPN callback installed and
+ // that callback doesn't properly handle ALPN.
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "error in ALPN selection callback");
+ return false;
+
+ case SSL_NEXT_PROTO_EARLY_VALUE:
+ MOZ_CRASH("Unexpected 0-RTT ALPN value");
+ return false;
+ }
+
+ // Warning: NSS won't null terminate the ALPN string for us.
+ std::string chosen(chosenAlpn, chosenAlpnLen);
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Selected ALPN string: " << chosen);
+ if (alpn_allowed_.find(chosen) == alpn_allowed_.end()) {
+ // Maybe our peer chose a protocol we didn't offer (when we are client), or
+ // something is seriously wrong.
+ std::ostringstream ss;
+ for (auto i = alpn_allowed_.begin(); i != alpn_allowed_.end(); ++i) {
+ ss << (i == alpn_allowed_.begin() ? " '" : ", '") << *i << "'";
+ }
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Bad ALPN string: '" << chosen
+ << "'; permitted:" << ss.str());
+ return false;
+ }
+ alpn_ = chosen;
+ return true;
+}
+
+
+void TransportLayerDtls::PacketReceived(TransportLayer* layer,
+ const unsigned char *data,
+ size_t len) {
+ CheckThread();
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")");
+
+ if (state_ != TS_CONNECTING && state_ != TS_OPEN) {
+ MOZ_MTLOG(ML_DEBUG,
+ LAYER_INFO << "Discarding packet in inappropriate state");
+ return;
+ }
+
+ // not DTLS per RFC 7983
+ if (data[0] < 20 || data[0] > 63) {
+ return;
+ }
+
+ nspr_io_adapter_->PacketReceived(data, len);
+
+ // If we're still connecting, try to handshake
+ if (state_ == TS_CONNECTING) {
+ Handshake();
+ }
+
+ // Now try a recv if we're open, since there might be data left
+ if (state_ == TS_OPEN) {
+ // nICEr uses a 9216 bytes buffer to allow support for jumbo frames
+ unsigned char buf[9216];
+ int32_t rv;
+ // One packet might contain several DTLS packets
+ do {
+ rv = PR_Recv(ssl_fd_.get(), buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT);
+ if (rv > 0) {
+ // We have data
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS");
+ SignalPacketReceived(this, buf, rv);
+ } else if (rv == 0) {
+ TL_SET_STATE(TS_CLOSED);
+ } else {
+ int32_t err = PR_GetError();
+
+ if (err == PR_WOULD_BLOCK_ERROR) {
+ // This gets ignored
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked");
+ } else {
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err);
+ TL_SET_STATE(TS_ERROR);
+ }
+ }
+ } while (rv > 0);
+ }
+}
+
+TransportResult TransportLayerDtls::SendPacket(const unsigned char *data,
+ size_t len) {
+ CheckThread();
+ if (state_ != TS_OPEN) {
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Can't call SendPacket() in state "
+ << state_);
+ return TE_ERROR;
+ }
+
+ int32_t rv = PR_Send(ssl_fd_.get(), data, len, 0, PR_INTERVAL_NO_WAIT);
+
+ if (rv > 0) {
+ // We have data
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Wrote " << rv << " bytes to SSL Layer");
+ return rv;
+ }
+
+ if (rv == 0) {
+ TL_SET_STATE(TS_CLOSED);
+ return 0;
+ }
+
+ int32_t err = PR_GetError();
+
+ if (err == PR_WOULD_BLOCK_ERROR) {
+ // This gets ignored
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Send would have blocked");
+ return TE_WOULDBLOCK;
+ }
+
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err);
+ TL_SET_STATE(TS_ERROR);
+ return TE_ERROR;
+}
+
+SECStatus TransportLayerDtls::GetClientAuthDataHook(void *arg, PRFileDesc *fd,
+ CERTDistNames *caNames,
+ CERTCertificate **pRetCert,
+ SECKEYPrivateKey **pRetKey) {
+ MOZ_MTLOG(ML_DEBUG, "Server requested client auth");
+
+ TransportLayerDtls *stream = reinterpret_cast<TransportLayerDtls *>(arg);
+ stream->CheckThread();
+
+ if (!stream->identity_) {
+ MOZ_MTLOG(ML_ERROR, "No identity available");
+ PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
+ return SECFailure;
+ }
+
+ *pRetCert = CERT_DupCertificate(stream->identity_->cert().get());
+ if (!*pRetCert) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return SECFailure;
+ }
+
+ *pRetKey = SECKEY_CopyPrivateKey(stream->identity_->privkey());
+ if (!*pRetKey) {
+ CERT_DestroyCertificate(*pRetCert);
+ *pRetCert = nullptr;
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+nsresult TransportLayerDtls::SetSrtpCiphers(std::vector<uint16_t> ciphers) {
+ // TODO: We should check these
+ srtp_ciphers_ = ciphers;
+
+ return NS_OK;
+}
+
+nsresult TransportLayerDtls::GetSrtpCipher(uint16_t *cipher) const {
+ CheckThread();
+ if (state_ != TS_OPEN) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ SECStatus rv = SSL_GetSRTPCipher(ssl_fd_.get(), cipher);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_DEBUG, "No SRTP cipher negotiated");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult TransportLayerDtls::ExportKeyingMaterial(const std::string& label,
+ bool use_context,
+ const std::string& context,
+ unsigned char *out,
+ unsigned int outlen) {
+ CheckThread();
+ if (state_ != TS_OPEN) {
+ MOZ_ASSERT(false, "Transport must be open for ExportKeyingMaterial");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ SECStatus rv = SSL_ExportKeyingMaterial(ssl_fd_.get(),
+ label.c_str(),
+ label.size(),
+ use_context,
+ reinterpret_cast<const unsigned char *>(
+ context.c_str()),
+ context.size(),
+ out,
+ outlen);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't export SSL keying material");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+SECStatus TransportLayerDtls::AuthCertificateHook(void *arg,
+ PRFileDesc *fd,
+ PRBool checksig,
+ PRBool isServer) {
+ TransportLayerDtls *stream = reinterpret_cast<TransportLayerDtls *>(arg);
+ stream->CheckThread();
+ return stream->AuthCertificateHook(fd, checksig, isServer);
+}
+
+SECStatus
+TransportLayerDtls::CheckDigest(const RefPtr<VerificationDigest>& digest,
+ UniqueCERTCertificate& peer_cert) const {
+ unsigned char computed_digest[kMaxDigestLength];
+ size_t computed_digest_len;
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Checking digest, algorithm="
+ << digest->algorithm_);
+ nsresult res =
+ DtlsIdentity::ComputeFingerprint(peer_cert,
+ digest->algorithm_,
+ computed_digest,
+ sizeof(computed_digest),
+ &computed_digest_len);
+ if (NS_FAILED(res)) {
+ MOZ_MTLOG(ML_ERROR, "Could not compute peer fingerprint for digest " <<
+ digest->algorithm_);
+ // Go to end
+ PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
+ return SECFailure;
+ }
+
+ if (computed_digest_len != digest->len_) {
+ MOZ_MTLOG(ML_ERROR, "Digest is wrong length " << digest->len_ <<
+ " should be " << computed_digest_len << " for algorithm " <<
+ digest->algorithm_);
+ PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
+ return SECFailure;
+ }
+
+ if (memcmp(digest->value_, computed_digest, computed_digest_len) != 0) {
+ MOZ_MTLOG(ML_ERROR, "Digest does not match");
+ PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+
+SECStatus TransportLayerDtls::AuthCertificateHook(PRFileDesc *fd,
+ PRBool checksig,
+ PRBool isServer) {
+ CheckThread();
+ UniqueCERTCertificate peer_cert(SSL_PeerCertificate(fd));
+
+ // We are not set up to take this being called multiple
+ // times. Change this if we ever add renegotiation.
+ MOZ_ASSERT(!auth_hook_called_);
+ if (auth_hook_called_) {
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return SECFailure;
+ }
+ auth_hook_called_ = true;
+
+ MOZ_ASSERT(verification_mode_ != VERIFY_UNSET);
+
+ switch (verification_mode_) {
+ case VERIFY_UNSET:
+ // Break out to error exit
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ break;
+
+ case VERIFY_ALLOW_ALL:
+ cert_ok_ = true;
+ return SECSuccess;
+
+ case VERIFY_DIGEST:
+ {
+ MOZ_ASSERT(digests_.size() != 0);
+ // Check all the provided digests
+
+ // Checking functions call PR_SetError()
+ SECStatus rv = SECFailure;
+ for (size_t i = 0; i < digests_.size(); i++) {
+ RefPtr<VerificationDigest> digest = digests_[i];
+ rv = CheckDigest(digest, peer_cert);
+
+ // Matches a digest, we are good to go
+ if (rv == SECSuccess) {
+ cert_ok_ = true;
+ return SECSuccess;
+ }
+ }
+ }
+ break;
+ default:
+ MOZ_CRASH(); // Can't happen
+ }
+
+ return SECFailure;
+}
+
+void TransportLayerDtls::TimerCallback(nsITimer *timer, void *arg) {
+ TransportLayerDtls *dtls = reinterpret_cast<TransportLayerDtls *>(arg);
+
+ MOZ_MTLOG(ML_DEBUG, "DTLS timer expired");
+
+ dtls->Handshake();
+}
+
+} // close namespace
diff --git a/media/mtransport/transportlayerdtls.h b/media/mtransport/transportlayerdtls.h
new file mode 100644
index 000000000..f0f86d490
--- /dev/null
+++ b/media/mtransport/transportlayerdtls.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef transportlayerdtls_h__
+#define transportlayerdtls_h__
+
+#include <queue>
+#include <set>
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+#include "ScopedNSSTypes.h"
+#include "m_cpp_utils.h"
+#include "dtlsidentity.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+struct Packet;
+
+class TransportLayerNSPRAdapter {
+ public:
+ explicit TransportLayerNSPRAdapter(TransportLayer *output) :
+ output_(output),
+ input_(),
+ enabled_(true) {}
+
+ void PacketReceived(const void *data, int32_t len);
+ int32_t Recv(void *buf, int32_t buflen);
+ int32_t Write(const void *buf, int32_t length);
+ void SetEnabled(bool enabled) { enabled_ = enabled; }
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerNSPRAdapter);
+
+ TransportLayer *output_;
+ std::queue<Packet *> input_;
+ bool enabled_;
+};
+
+class TransportLayerDtls final : public TransportLayer {
+ public:
+ TransportLayerDtls() :
+ role_(CLIENT),
+ verification_mode_(VERIFY_UNSET),
+ ssl_fd_(nullptr),
+ auth_hook_called_(false),
+ cert_ok_(false) {}
+
+ virtual ~TransportLayerDtls();
+
+ enum Role { CLIENT, SERVER};
+ enum Verification { VERIFY_UNSET, VERIFY_ALLOW_ALL, VERIFY_DIGEST};
+ const static size_t kMaxDigestLength = HASH_LENGTH_MAX;
+
+ // DTLS-specific operations
+ void SetRole(Role role) { role_ = role;}
+ Role role() { return role_; }
+
+ void SetIdentity(const RefPtr<DtlsIdentity>& identity) {
+ identity_ = identity;
+ }
+ nsresult SetAlpn(const std::set<std::string>& allowedAlpn,
+ const std::string& alpnDefault);
+ const std::string& GetNegotiatedAlpn() const { return alpn_; }
+
+ nsresult SetVerificationAllowAll();
+ nsresult SetVerificationDigest(const std::string digest_algorithm,
+ const unsigned char *digest_value,
+ size_t digest_len);
+
+ nsresult GetCipherSuite(uint16_t* cipherSuite) const;
+
+ nsresult SetSrtpCiphers(std::vector<uint16_t> ciphers);
+ nsresult GetSrtpCipher(uint16_t *cipher) const;
+
+ nsresult ExportKeyingMaterial(const std::string& label,
+ bool use_context,
+ const std::string& context,
+ unsigned char *out,
+ unsigned int outlen);
+
+ // Transport layer overrides.
+ virtual nsresult InitInternal();
+ virtual void WasInserted();
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+ // Signals
+ void StateChange(TransportLayer *layer, State state);
+ void PacketReceived(TransportLayer* layer, const unsigned char *data,
+ size_t len);
+
+ // For testing use only. Returns the fd.
+ PRFileDesc* internal_fd() { CheckThread(); return ssl_fd_.get(); }
+
+ TRANSPORT_LAYER_ID("dtls")
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerDtls);
+
+ // A single digest to check
+ class VerificationDigest {
+ public:
+ VerificationDigest(std::string algorithm,
+ const unsigned char *value, size_t len) {
+ MOZ_ASSERT(len <= sizeof(value_));
+
+ algorithm_ = algorithm;
+ memcpy(value_, value, len);
+ len_ = len;
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VerificationDigest)
+
+ std::string algorithm_;
+ size_t len_;
+ unsigned char value_[kMaxDigestLength];
+
+ private:
+ ~VerificationDigest() {}
+ DISALLOW_COPY_ASSIGN(VerificationDigest);
+ };
+
+
+ bool Setup();
+ bool SetupCipherSuites(UniquePRFileDesc& ssl_fd) const;
+ bool SetupAlpn(UniquePRFileDesc& ssl_fd) const;
+ void Handshake();
+
+ bool CheckAlpn();
+
+ static SECStatus GetClientAuthDataHook(void *arg, PRFileDesc *fd,
+ CERTDistNames *caNames,
+ CERTCertificate **pRetCert,
+ SECKEYPrivateKey **pRetKey);
+ static SECStatus AuthCertificateHook(void *arg,
+ PRFileDesc *fd,
+ PRBool checksig,
+ PRBool isServer);
+ SECStatus AuthCertificateHook(PRFileDesc *fd,
+ PRBool checksig,
+ PRBool isServer);
+
+ static void TimerCallback(nsITimer *timer, void *arg);
+
+ SECStatus CheckDigest(const RefPtr<VerificationDigest>& digest,
+ UniqueCERTCertificate& cert) const;
+
+ RefPtr<DtlsIdentity> identity_;
+ // What ALPN identifiers are permitted.
+ std::set<std::string> alpn_allowed_;
+ // What ALPN identifier is used if ALPN is not supported.
+ // The empty string indicates that ALPN is required.
+ std::string alpn_default_;
+ // What ALPN string was negotiated.
+ std::string alpn_;
+ std::vector<uint16_t> srtp_ciphers_;
+
+ Role role_;
+ Verification verification_mode_;
+ std::vector<RefPtr<VerificationDigest> > digests_;
+
+ // Must delete nspr_io_adapter after ssl_fd_ b/c ssl_fd_ causes an alert
+ // (ssl_fd_ contains an un-owning pointer to nspr_io_adapter_)
+ UniquePtr<TransportLayerNSPRAdapter> nspr_io_adapter_;
+ UniquePRFileDesc ssl_fd_;
+
+ nsCOMPtr<nsITimer> timer_;
+ bool auth_hook_called_;
+ bool cert_ok_;
+};
+
+
+} // close namespace
+#endif
diff --git a/media/mtransport/transportlayerice.cpp b/media/mtransport/transportlayerice.cpp
new file mode 100644
index 000000000..dba2c5424
--- /dev/null
+++ b/media/mtransport/transportlayerice.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+// Some of this code is cut-and-pasted from nICEr. Copyright is:
+
+/*
+Copyright (c) 2007, Adobe Systems, Incorporated
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* Neither the name of Adobe Systems, Network Resonance nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <string>
+#include <vector>
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsError.h"
+#include "nsIEventTarget.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "registry.h"
+#include "async_timer.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_client_ctx.h"
+#include "stun_server_ctx.h"
+#include "ice_ctx.h"
+#include "ice_candidate.h"
+#include "ice_handler.h"
+}
+
+// Local includes
+#include "logging.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
+#include "transportflow.h"
+#include "transportlayerice.h"
+
+namespace mozilla {
+
+#ifdef ERROR
+#undef ERROR
+#endif
+
+MOZ_MTLOG_MODULE("mtransport")
+
+TransportLayerIce::TransportLayerIce(const std::string& name)
+ : name_(name),
+ ctx_(nullptr), stream_(nullptr), component_(0),
+ old_stream_(nullptr)
+{
+ // setup happens later
+}
+
+TransportLayerIce::~TransportLayerIce() {
+ // No need to do anything here, since we use smart pointers
+}
+
+void TransportLayerIce::SetParameters(RefPtr<NrIceCtx> ctx,
+ RefPtr<NrIceMediaStream> stream,
+ int component) {
+ // If SetParameters is called and we already have a stream_, this means
+ // we're handling an ICE restart. We need to hold the old stream until
+ // we know the new stream is working.
+ if (stream_ && !old_stream_ && (stream_ != stream)) {
+ // Here we leave the old stream's signals connected until we don't need
+ // it anymore. They will be disconnected if ice restart is successful.
+ old_stream_ = stream_;
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "SetParameters save old stream("
+ << old_stream_->name() << ")");
+ }
+
+ ctx_ = ctx;
+ stream_ = stream;
+ component_ = component;
+
+ PostSetup();
+}
+
+void TransportLayerIce::PostSetup() {
+ target_ = ctx_->thread();
+
+ stream_->SignalReady.connect(this, &TransportLayerIce::IceReady);
+ stream_->SignalFailed.connect(this, &TransportLayerIce::IceFailed);
+ stream_->SignalPacketReceived.connect(this,
+ &TransportLayerIce::IcePacketReceived);
+ if (stream_->state() == NrIceMediaStream::ICE_OPEN) {
+ TL_SET_STATE(TS_OPEN);
+ }
+}
+
+void TransportLayerIce::ResetOldStream() {
+ if (old_stream_ == nullptr) {
+ return; // no work to do
+ }
+ // ICE restart successful on the new stream, we can forget the old stream now
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "ResetOldStream(" << old_stream_->name()
+ << ")");
+ old_stream_->SignalReady.disconnect(this);
+ old_stream_->SignalFailed.disconnect(this);
+ old_stream_->SignalPacketReceived.disconnect(this);
+ old_stream_ = nullptr;
+}
+
+void TransportLayerIce::RestoreOldStream() {
+ if (old_stream_ == nullptr) {
+ return; // no work to do
+ }
+ // ICE restart rollback, we need to restore the old stream
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "RestoreOldStream(" << old_stream_->name()
+ << ")");
+ stream_->SignalReady.disconnect(this);
+ stream_->SignalFailed.disconnect(this);
+ stream_->SignalPacketReceived.disconnect(this);
+ stream_ = old_stream_;
+ old_stream_ = nullptr;
+
+ if (stream_->state() == NrIceMediaStream::ICE_OPEN) {
+ IceReady(stream_);
+ } else if (stream_->state() == NrIceMediaStream::ICE_CLOSED) {
+ IceFailed(stream_);
+ }
+ // No events are fired when the stream is ICE_CONNECTING. If the
+ // restored stream is ICE_CONNECTING, IceReady/IceFailed will fire
+ // later.
+}
+
+TransportResult TransportLayerIce::SendPacket(const unsigned char *data,
+ size_t len) {
+ CheckThread();
+ // use old_stream_ until stream_ is ready
+ nsresult res = (old_stream_?old_stream_:stream_)->SendPacket(component_,
+ data,
+ len);
+
+ if (!NS_SUCCEEDED(res)) {
+ return (res == NS_BASE_STREAM_WOULD_BLOCK) ?
+ TE_WOULDBLOCK : TE_ERROR;
+ }
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " SendPacket(" << len << ") succeeded");
+
+ return len;
+}
+
+
+void TransportLayerIce::IceCandidate(NrIceMediaStream *stream,
+ const std::string&) {
+ // NO-OP for now
+}
+
+void TransportLayerIce::IceReady(NrIceMediaStream *stream) {
+ CheckThread();
+ // only handle the current stream (not the old stream during restart)
+ if (stream != stream_) {
+ return;
+ }
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "ICE Ready(" << stream->name() << ","
+ << component_ << ")");
+ TL_SET_STATE(TS_OPEN);
+}
+
+void TransportLayerIce::IceFailed(NrIceMediaStream *stream) {
+ CheckThread();
+ // only handle the current stream (not the old stream during restart)
+ if (stream != stream_) {
+ return;
+ }
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "ICE Failed(" << stream->name() << ","
+ << component_ << ")");
+ TL_SET_STATE(TS_ERROR);
+}
+
+void TransportLayerIce::IcePacketReceived(NrIceMediaStream *stream, int component,
+ const unsigned char *data, int len) {
+ CheckThread();
+ // We get packets for both components, so ignore the ones that aren't
+ // for us.
+ if (component_ != component)
+ return;
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << stream->name() << ","
+ << component << "," << len << ")");
+ SignalPacketReceived(this, data, len);
+}
+
+} // close namespace
diff --git a/media/mtransport/transportlayerice.h b/media/mtransport/transportlayerice.h
new file mode 100644
index 000000000..2489dc145
--- /dev/null
+++ b/media/mtransport/transportlayerice.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+// This is a wrapper around the nICEr ICE stack
+#ifndef transportlayerice_h__
+#define transportlayerice_h__
+
+#include <vector>
+
+#include "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsITimer.h"
+
+#include "m_cpp_utils.h"
+
+#include "nricemediastream.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+// An ICE transport layer -- corresponds to a single ICE
+namespace mozilla {
+
+class TransportLayerIce : public TransportLayer {
+ public:
+ explicit TransportLayerIce(const std::string& name);
+
+ virtual ~TransportLayerIce();
+
+ void SetParameters(RefPtr<NrIceCtx> ctx,
+ RefPtr<NrIceMediaStream> stream,
+ int component);
+
+ void ResetOldStream(); // called after successful ice restart
+ void RestoreOldStream(); // called after unsuccessful ice restart
+
+ // Transport layer overrides.
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+ // Slots for ICE
+ void IceCandidate(NrIceMediaStream *stream, const std::string&);
+ void IceReady(NrIceMediaStream *stream);
+ void IceFailed(NrIceMediaStream *stream);
+ void IcePacketReceived(NrIceMediaStream *stream, int component,
+ const unsigned char *data, int len);
+
+ TRANSPORT_LAYER_ID("ice")
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerIce);
+ void PostSetup();
+
+ const std::string name_;
+ RefPtr<NrIceCtx> ctx_;
+ RefPtr<NrIceMediaStream> stream_;
+ int component_;
+
+ // used to hold the old stream
+ RefPtr<NrIceMediaStream> old_stream_;
+};
+
+} // close namespace
+#endif
diff --git a/media/mtransport/transportlayerlog.cpp b/media/mtransport/transportlayerlog.cpp
new file mode 100644
index 000000000..556103d8c
--- /dev/null
+++ b/media/mtransport/transportlayerlog.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include "logging.h"
+#include "transportflow.h"
+#include "transportlayerlog.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+void TransportLayerLogging::WasInserted() {
+ if (downward_) {
+ downward_->SignalStateChange.connect(
+ this, &TransportLayerLogging::StateChange);
+ downward_->SignalPacketReceived.connect(
+ this, &TransportLayerLogging::PacketReceived);
+ TL_SET_STATE(downward_->state());
+ }
+}
+
+TransportResult
+TransportLayerLogging::SendPacket(const unsigned char *data, size_t len) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << len << ")");
+
+ if (downward_) {
+ return downward_->SendPacket(data, len);
+ }
+ else {
+ return static_cast<TransportResult>(len);
+ }
+}
+
+void TransportLayerLogging::StateChange(TransportLayer *layer, State state) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Received StateChange to " << state);
+
+ TL_SET_STATE(state);
+}
+
+void TransportLayerLogging::PacketReceived(TransportLayer* layer,
+ const unsigned char *data,
+ size_t len) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")");
+
+ SignalPacketReceived(this, data, len);
+}
+
+
+
+} // close namespace
diff --git a/media/mtransport/transportlayerlog.h b/media/mtransport/transportlayerlog.h
new file mode 100644
index 000000000..9992e06af
--- /dev/null
+++ b/media/mtransport/transportlayerlog.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef transportlayerlog_h__
+#define transportlayerlog_h__
+
+#include "m_cpp_utils.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+class TransportLayerLogging : public TransportLayer {
+public:
+ TransportLayerLogging() {}
+
+ // Overrides for TransportLayer
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+ // Signals (forwarded to upper layer)
+ void StateChange(TransportLayer *layer, State state);
+ void PacketReceived(TransportLayer* layer, const unsigned char *data,
+ size_t len);
+
+ TRANSPORT_LAYER_ID("log")
+
+protected:
+ virtual void WasInserted();
+
+private:
+ DISALLOW_COPY_ASSIGN(TransportLayerLogging);
+};
+
+
+} // close namespace
+#endif
diff --git a/media/mtransport/transportlayerloopback.cpp b/media/mtransport/transportlayerloopback.cpp
new file mode 100644
index 000000000..11c3b4244
--- /dev/null
+++ b/media/mtransport/transportlayerloopback.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include "logging.h"
+#include "nspr.h"
+#include "prlock.h"
+
+#include "nsNetCID.h"
+#include "nsIComponentManager.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIEventTarget.h"
+#include "nsIIOService.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransportService.h"
+#include "nsServiceManagerUtils.h"
+
+#include "transportflow.h"
+#include "transportlayerloopback.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+nsresult TransportLayerLoopback::Init() {
+ timer_ = do_CreateInstance(NS_TIMER_CONTRACTID);
+ MOZ_ASSERT(timer_);
+ if (!timer_)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+ target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (!NS_SUCCEEDED(rv))
+ return rv;
+
+ timer_->SetTarget(target_);
+
+ packets_lock_ = PR_NewLock();
+ MOZ_ASSERT(packets_lock_);
+ if (!packets_lock_)
+ return NS_ERROR_FAILURE;
+
+ deliverer_ = new Deliverer(this);
+
+ timer_->InitWithCallback(deliverer_, 100, nsITimer::TYPE_REPEATING_SLACK);
+
+ return NS_OK;
+}
+
+// Connect to the other side
+void TransportLayerLoopback::Connect(TransportLayerLoopback* peer) {
+ peer_ = peer;
+
+ TL_SET_STATE(TS_OPEN);
+}
+
+TransportResult
+TransportLayerLoopback::SendPacket(const unsigned char *data, size_t len) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << len << ")");
+
+ if (!peer_) {
+ MOZ_MTLOG(ML_ERROR, "Discarding packet because peer not attached");
+ return TE_ERROR;
+ }
+
+ nsresult res = peer_->QueuePacket(data, len);
+ if (!NS_SUCCEEDED(res))
+ return TE_ERROR;
+
+ return static_cast<TransportResult>(len);
+}
+
+nsresult TransportLayerLoopback::QueuePacket(const unsigned char *data,
+ size_t len) {
+ MOZ_ASSERT(packets_lock_);
+
+ PR_Lock(packets_lock_);
+
+ if (combinePackets_ && !packets_.empty()) {
+ QueuedPacket *packet = packets_.front();
+ packets_.pop();
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing combined packets of length " << packet->len() << " and " << len);
+ packets_.push(new QueuedPacket());
+ packets_.back()->Assign(packet->data(), packet->len(),
+ data, len);
+ } else {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing packet of length " << len);
+ packets_.push(new QueuedPacket());
+ packets_.back()->Assign(data, len);
+ }
+
+ PRStatus r = PR_Unlock(packets_lock_);
+ MOZ_ASSERT(r == PR_SUCCESS);
+ if (r != PR_SUCCESS)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+void TransportLayerLoopback::DeliverPackets() {
+ while (!packets_.empty()) {
+ QueuedPacket *packet = packets_.front();
+ packets_.pop();
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Delivering packet of length " <<
+ packet->len());
+ SignalPacketReceived(this, packet->data(), packet->len());
+
+ delete packet;
+ }
+}
+
+NS_IMPL_ISUPPORTS(TransportLayerLoopback::Deliverer, nsITimerCallback)
+
+NS_IMETHODIMP TransportLayerLoopback::Deliverer::Notify(nsITimer *timer) {
+ if (!layer_)
+ return NS_OK;
+
+ layer_->DeliverPackets();
+
+ return NS_OK;
+}
+} // close namespace
diff --git a/media/mtransport/transportlayerloopback.h b/media/mtransport/transportlayerloopback.h
new file mode 100644
index 000000000..8ad5e2865
--- /dev/null
+++ b/media/mtransport/transportlayerloopback.h
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef transportlayerloopback_h__
+#define transportlayerloopback_h__
+
+#include "nspr.h"
+#include "prio.h"
+#include "prlock.h"
+
+#include <memory>
+#include <queue>
+
+
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsITimer.h"
+
+
+#include "m_cpp_utils.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+// A simple loopback transport layer that is used for testing.
+namespace mozilla {
+
+class TransportLayerLoopback : public TransportLayer {
+ public:
+ TransportLayerLoopback() :
+ peer_(nullptr),
+ timer_(nullptr),
+ packets_(),
+ packets_lock_(nullptr),
+ deliverer_(nullptr),
+ combinePackets_(false) {}
+
+ ~TransportLayerLoopback() {
+ while (!packets_.empty()) {
+ QueuedPacket *packet = packets_.front();
+ packets_.pop();
+ delete packet;
+ }
+ if (packets_lock_) {
+ PR_DestroyLock(packets_lock_);
+ }
+ timer_->Cancel();
+ deliverer_->Detach();
+ }
+
+ // Init
+ nsresult Init();
+
+ // Connect to the other side
+ void Connect(TransportLayerLoopback* peer);
+
+ // Disconnect
+ void Disconnect() {
+ TransportLayerLoopback *peer = peer_;
+
+ peer_ = nullptr;
+ if (peer) {
+ peer->Disconnect();
+ }
+ }
+
+ void CombinePackets(bool combine) { combinePackets_ = combine; }
+
+ // Overrides for TransportLayer
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+ // Deliver queued packets
+ void DeliverPackets();
+
+ TRANSPORT_LAYER_ID("loopback")
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerLoopback);
+
+ // A queued packet
+ class QueuedPacket {
+ public:
+ QueuedPacket() : data_(nullptr), len_(0) {}
+ ~QueuedPacket() {
+ delete [] data_;
+ }
+
+ void Assign(const unsigned char *data, size_t len) {
+ data_ = new unsigned char[len];
+ memcpy(static_cast<void *>(data_),
+ static_cast<const void *>(data), len);
+ len_ = len;
+ }
+
+ void Assign(const unsigned char *data1, size_t len1,
+ const unsigned char *data2, size_t len2) {
+ data_ = new unsigned char[len1 + len2];
+ memcpy(static_cast<void *>(data_),
+ static_cast<const void *>(data1), len1);
+ memcpy(static_cast<void *>(data_ + len1),
+ static_cast<const void *>(data2), len2);
+ len_ = len1 + len2;
+ }
+
+ const unsigned char *data() const { return data_; }
+ size_t len() const { return len_; }
+
+ private:
+ DISALLOW_COPY_ASSIGN(QueuedPacket);
+
+ unsigned char *data_;
+ size_t len_;
+ };
+
+ // A timer to deliver packets if some are available
+ // Fires every 100 ms
+ class Deliverer : public nsITimerCallback {
+ public:
+ explicit Deliverer(TransportLayerLoopback *layer) :
+ layer_(layer) {}
+ void Detach() {
+ layer_ = nullptr;
+ }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+ private:
+ virtual ~Deliverer() {
+ }
+
+ DISALLOW_COPY_ASSIGN(Deliverer);
+
+ TransportLayerLoopback *layer_;
+ };
+
+ // Queue a packet for delivery
+ nsresult QueuePacket(const unsigned char *data, size_t len);
+
+ TransportLayerLoopback* peer_;
+ nsCOMPtr<nsITimer> timer_;
+ std::queue<QueuedPacket *> packets_;
+ PRLock *packets_lock_;
+ RefPtr<Deliverer> deliverer_;
+ bool combinePackets_;
+};
+
+} // close namespace
+#endif
diff --git a/media/mtransport/transportlayerprsock.cpp b/media/mtransport/transportlayerprsock.cpp
new file mode 100644
index 000000000..ccaff32d1
--- /dev/null
+++ b/media/mtransport/transportlayerprsock.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#include "logging.h"
+#include "nspr.h"
+#include "prerror.h"
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include "nsASocketHandler.h"
+#include "nsISocketTransportService.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPCOM.h"
+
+#include "transportflow.h"
+#include "transportlayerprsock.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+nsresult TransportLayerPrsock::InitInternal() {
+ // Get the transport service as a transport service
+ nsresult rv;
+ stservice_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+ if (!NS_SUCCEEDED(rv)) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get socket transport service");
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void TransportLayerPrsock::Import(PRFileDesc *fd, nsresult *result) {
+ if (state_ != TS_INIT) {
+ *result = NS_ERROR_NOT_INITIALIZED;
+ return;
+ }
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Importing()");
+ fd_ = fd;
+ handler_ = new SocketHandler(this, fd);
+
+ nsresult rv = stservice_->AttachSocket(fd_, handler_);
+ if (!NS_SUCCEEDED(rv)) {
+ *result = rv;
+ return;
+ }
+
+ TL_SET_STATE(TS_OPEN);
+
+ *result = NS_OK;
+}
+
+int TransportLayerPrsock::SendPacket(const unsigned char *data, size_t len) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << len << ")");
+ if (state_ != TS_OPEN) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Can't send packet on closed interface");
+ return TE_INTERNAL;
+ }
+
+ int32_t status;
+ status = PR_Write(fd_, data, len);
+ if (status >= 0) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Wrote " << len << " bytes");
+ return status;
+ }
+
+ PRErrorCode err = PR_GetError();
+ if (err == PR_WOULD_BLOCK_ERROR) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Write blocked");
+ return TE_WOULDBLOCK;
+ }
+
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Write error; channel closed");
+ TL_SET_STATE(TS_ERROR);
+ return TE_ERROR;
+}
+
+void TransportLayerPrsock::OnSocketReady(PRFileDesc *fd, int16_t outflags) {
+ if (!(outflags & PR_POLL_READ)) {
+ return;
+ }
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "OnSocketReady(flags=" << outflags << ")");
+
+ unsigned char buf[1600];
+ int32_t rv = PR_Read(fd, buf, sizeof(buf));
+
+ if (rv > 0) {
+ // Successful read
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes");
+ SignalPacketReceived(this, buf, rv);
+ } else if (rv == 0) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read 0 bytes; channel closed");
+ TL_SET_STATE(TS_CLOSED);
+ } else {
+ PRErrorCode err = PR_GetError();
+
+ if (err != PR_WOULD_BLOCK_ERROR) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read error; channel closed");
+ TL_SET_STATE(TS_ERROR);
+ }
+ }
+}
+
+NS_IMPL_ISUPPORTS0(TransportLayerPrsock::SocketHandler)
+} // close namespace
diff --git a/media/mtransport/transportlayerprsock.h b/media/mtransport/transportlayerprsock.h
new file mode 100644
index 000000000..ac732c946
--- /dev/null
+++ b/media/mtransport/transportlayerprsock.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef transportlayerprsock_h__
+#define transportlayerprsock_h__
+
+#include "nspr.h"
+#include "prio.h"
+
+#include "nsASocketHandler.h"
+#include "nsCOMPtr.h"
+#include "nsISocketTransportService.h"
+#include "nsXPCOM.h"
+
+#include "m_cpp_utils.h"
+#include "transportflow.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+class TransportLayerPrsock : public TransportLayer {
+ public:
+ TransportLayerPrsock() : fd_(nullptr), handler_() {}
+
+ virtual ~TransportLayerPrsock() {
+ Detach();
+ }
+
+
+ // Internal initializer
+ virtual nsresult InitInternal();
+
+ void Import(PRFileDesc *fd, nsresult *result);
+
+ void Detach() {
+ handler_->Detach();
+ }
+
+ // Implement TransportLayer
+ virtual TransportResult SendPacket(const unsigned char *data, size_t len);
+
+ TRANSPORT_LAYER_ID("prsock")
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerPrsock);
+
+ // Inner class
+ class SocketHandler : public nsASocketHandler {
+ public:
+ SocketHandler(TransportLayerPrsock *prsock, PRFileDesc *fd) :
+ prsock_(prsock), fd_(fd) {
+ mPollFlags = PR_POLL_READ;
+ }
+
+ void Detach() {
+ mCondition = NS_BASE_STREAM_CLOSED;
+ prsock_ = nullptr;
+ }
+
+ // Implement nsASocket
+ virtual void OnSocketReady(PRFileDesc *fd, int16_t outflags) override {
+ if (prsock_) {
+ prsock_->OnSocketReady(fd, outflags);
+ }
+ }
+
+ virtual void OnSocketDetached(PRFileDesc *fd) override {
+ if (prsock_) {
+ prsock_->OnSocketDetached(fd);
+ }
+ PR_Close(fd_);
+ }
+
+ virtual void IsLocal(bool *aIsLocal) override {
+ // TODO(jesup): better check? Does it matter? (likely no)
+ *aIsLocal = false;
+ }
+
+ virtual uint64_t ByteCountSent() override { return 0; }
+ virtual uint64_t ByteCountReceived() override { return 0; }
+
+ // nsISupports methods
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ private:
+ TransportLayerPrsock *prsock_;
+ PRFileDesc *fd_;
+ private:
+ DISALLOW_COPY_ASSIGN(SocketHandler);
+ virtual ~SocketHandler() {}
+ };
+
+ // Allow SocketHandler to talk to our APIs
+ friend class SocketHandler;
+
+ // Functions to be called by SocketHandler
+ void OnSocketReady(PRFileDesc *fd, int16_t outflags);
+ void OnSocketDetached(PRFileDesc *fd) {
+ TL_SET_STATE(TS_CLOSED);
+ }
+ void IsLocal(bool *aIsLocal) {
+ // TODO(jesup): better check? Does it matter? (likely no)
+ *aIsLocal = false;
+ }
+
+ PRFileDesc *fd_;
+ RefPtr<SocketHandler> handler_;
+ nsCOMPtr<nsISocketTransportService> stservice_;
+
+};
+
+
+
+} // close namespace
+#endif