summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/transport
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/webrtc/transport/README45
-rw-r--r--dom/media/webrtc/transport/SrtpFlow.cpp259
-rw-r--r--dom/media/webrtc/transport/SrtpFlow.h69
-rw-r--r--dom/media/webrtc/transport/WebrtcTCPSocketWrapper.cpp123
-rw-r--r--dom/media/webrtc/transport/WebrtcTCPSocketWrapper.h69
-rw-r--r--dom/media/webrtc/transport/build/moz.build44
-rw-r--r--dom/media/webrtc/transport/common.build94
-rw-r--r--dom/media/webrtc/transport/dtlsidentity.cpp324
-rw-r--r--dom/media/webrtc/transport/dtlsidentity.h101
-rw-r--r--dom/media/webrtc/transport/fuzztest/moz.build31
-rw-r--r--dom/media/webrtc/transport/fuzztest/stun_parser_libfuzz.cpp35
-rw-r--r--dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h54
-rw-r--r--dom/media/webrtc/transport/ipc/PStunAddrsParams.h33
-rw-r--r--dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl35
-rw-r--r--dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl42
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp45
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h64
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp262
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h82
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh23
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp785
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h104
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h28
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp96
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h47
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp11
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h20
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp122
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h59
-rw-r--r--dom/media/webrtc/transport/ipc/moz.build54
-rw-r--r--dom/media/webrtc/transport/logging.h65
-rw-r--r--dom/media/webrtc/transport/m_cpp_utils.h25
-rw-r--r--dom/media/webrtc/transport/mdns_service/Cargo.toml14
-rw-r--r--dom/media/webrtc/transport/mdns_service/mdns_service.h28
-rw-r--r--dom/media/webrtc/transport/mdns_service/src/lib.rs843
-rw-r--r--dom/media/webrtc/transport/mediapacket.cpp145
-rw-r--r--dom/media/webrtc/transport/mediapacket.h117
-rw-r--r--dom/media/webrtc/transport/moz.build23
-rw-r--r--dom/media/webrtc/transport/nr_socket_proxy_config.cpp34
-rw-r--r--dom/media/webrtc/transport/nr_socket_proxy_config.h41
-rw-r--r--dom/media/webrtc/transport/nr_socket_prsock.cpp1785
-rw-r--r--dom/media/webrtc/transport/nr_socket_prsock.h320
-rw-r--r--dom/media/webrtc/transport/nr_socket_tcp.cpp310
-rw-r--r--dom/media/webrtc/transport/nr_socket_tcp.h117
-rw-r--r--dom/media/webrtc/transport/nr_timer.cpp256
-rw-r--r--dom/media/webrtc/transport/nricectx.cpp1106
-rw-r--r--dom/media/webrtc/transport/nricectx.h421
-rw-r--r--dom/media/webrtc/transport/nricemediastream.cpp709
-rw-r--r--dom/media/webrtc/transport/nricemediastream.h225
-rw-r--r--dom/media/webrtc/transport/nriceresolver.cpp234
-rw-r--r--dom/media/webrtc/transport/nriceresolver.h119
-rw-r--r--dom/media/webrtc/transport/nriceresolverfake.cpp174
-rw-r--r--dom/media/webrtc/transport/nriceresolverfake.h137
-rw-r--r--dom/media/webrtc/transport/nricestunaddr.cpp93
-rw-r--r--dom/media/webrtc/transport/nricestunaddr.h36
-rw-r--r--dom/media/webrtc/transport/nrinterfaceprioritizer.cpp258
-rw-r--r--dom/media/webrtc/transport/nrinterfaceprioritizer.h17
-rw-r--r--dom/media/webrtc/transport/rlogconnector.cpp186
-rw-r--r--dom/media/webrtc/transport/rlogconnector.h127
-rw-r--r--dom/media/webrtc/transport/runnable_utils.h221
-rw-r--r--dom/media/webrtc/transport/sigslot.h619
-rw-r--r--dom/media/webrtc/transport/simpletokenbucket.cpp60
-rw-r--r--dom/media/webrtc/transport/simpletokenbucket.h54
-rw-r--r--dom/media/webrtc/transport/srtp/README_MOZILLA7
-rw-r--r--dom/media/webrtc/transport/srtp/moz.build8
-rw-r--r--dom/media/webrtc/transport/stun_socket_filter.cpp432
-rw-r--r--dom/media/webrtc/transport/stun_socket_filter.h41
-rw-r--r--dom/media/webrtc/transport/test/TestSyncRunnable.cpp56
-rw-r--r--dom/media/webrtc/transport/test/buffered_stun_socket_unittest.cpp245
-rw-r--r--dom/media/webrtc/transport/test/dummysocket.h217
-rw-r--r--dom/media/webrtc/transport/test/gtest_ringbuffer_dumper.h78
-rw-r--r--dom/media/webrtc/transport/test/gtest_utils.h201
-rw-r--r--dom/media/webrtc/transport/test/ice_unittest.cpp4440
-rw-r--r--dom/media/webrtc/transport/test/moz.build104
-rw-r--r--dom/media/webrtc/transport/test/mtransport_test_utils.h47
-rw-r--r--dom/media/webrtc/transport/test/multi_tcp_socket_unittest.cpp515
-rw-r--r--dom/media/webrtc/transport/test/nrappkit_unittest.cpp126
-rw-r--r--dom/media/webrtc/transport/test/proxy_tunnel_socket_unittest.cpp277
-rw-r--r--dom/media/webrtc/transport/test/rlogconnector_unittest.cpp255
-rw-r--r--dom/media/webrtc/transport/test/runnable_utils_unittest.cpp343
-rw-r--r--dom/media/webrtc/transport/test/sctp_unittest.cpp382
-rw-r--r--dom/media/webrtc/transport/test/simpletokenbucket_unittest.cpp114
-rw-r--r--dom/media/webrtc/transport/test/sockettransportservice_unittest.cpp181
-rw-r--r--dom/media/webrtc/transport/test/stunserver.cpp652
-rw-r--r--dom/media/webrtc/transport/test/stunserver.h123
-rw-r--r--dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp415
-rw-r--r--dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp808
-rw-r--r--dom/media/webrtc/transport/test/transport_unittests.cpp1416
-rw-r--r--dom/media/webrtc/transport/test/turn_unittest.cpp440
-rw-r--r--dom/media/webrtc/transport/test/webrtcproxychannel_unittest.cpp739
-rw-r--r--dom/media/webrtc/transport/test_nr_socket.cpp1135
-rw-r--r--dom/media/webrtc/transport/test_nr_socket.h370
-rw-r--r--dom/media/webrtc/transport/third_party/import.py40
-rw-r--r--dom/media/webrtc/transport/third_party/moz.build40
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/IMPORT_FILES73
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/README74
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/README_MOZILLA11
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/nicer.gyp276
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.c67
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.h52
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c1052
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.h124
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.c689
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.h101
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_codeword.h41
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.c1786
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.h111
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c1125
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h188
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h84
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c1087
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h146
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_parser.c564
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c875
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.h101
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_reg.h81
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.c404
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.h98
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.c70
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.h62
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.c88
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.h66
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.c85
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.h96
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.c187
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.h123
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_local.h41
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c642
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.h53
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.c82
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.h63
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.c559
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.h112
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.c230
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.h46
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c110
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h13
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c285
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h45
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c210
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h13
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c176
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h43
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c656
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h66
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c195
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h48
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h218
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c611
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h147
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c888
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h200
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c1550
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h78
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c245
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h44
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c364
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h208
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c554
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h53
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h58
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c468
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h80
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c352
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h62
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c1277
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h161
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.c57
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.h41
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.c71
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.h41
-rw-r--r--dom/media/webrtc/transport/third_party/nICEr/upstream.diff2449
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/COPYRIGHT159
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/IMPORT_FILES87
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/README133
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/README_MOZILLA22
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/VERSION1
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/nrappkit.gyp251
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/event/async_timer.h54
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h83
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait_int.h62
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c696
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.h85
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/plugin/nr_plugin.h57
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/android_funcs.h62
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/csi_platform.h55
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/sys/ttycom.h38
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/android/port-impl.mk31
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h57
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include/sys/queue.h562
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h55
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/linux_funcs.h62
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h38
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/port-impl.mk31
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h107
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.c320
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.h96
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c604
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.h154
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_int.h97
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_local.c1168
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_vtbl.h96
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/registry/registrycb.c440
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_api.h51
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_common.h108
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_reg_keys.h167
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/stats/nrstats.h118
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.c73
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.h47
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.c109
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.h47
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/assoc.h90
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.c127
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.h94
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.c539
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.h126
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_common.h100
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c175
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.h14
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.c248
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.h108
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_defaults.h91
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.c136
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.h127
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_includes.h98
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.c273
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.h106
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h137
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.c198
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.h101
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_replace.c107
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_thread.h68
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.c235
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h109
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_types.h213
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.c215
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.h72
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c775
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h73
-rw-r--r--dom/media/webrtc/transport/third_party/nrappkit/upstream.diff391
-rw-r--r--dom/media/webrtc/transport/transportflow.cpp74
-rw-r--r--dom/media/webrtc/transport/transportflow.h105
-rw-r--r--dom/media/webrtc/transport/transportlayer.cpp49
-rw-r--r--dom/media/webrtc/transport/transportlayer.h108
-rw-r--r--dom/media/webrtc/transport/transportlayerdtls.cpp1558
-rw-r--r--dom/media/webrtc/transport/transportlayerdtls.h187
-rw-r--r--dom/media/webrtc/transport/transportlayerice.cpp168
-rw-r--r--dom/media/webrtc/transport/transportlayerice.h60
-rw-r--r--dom/media/webrtc/transport/transportlayerlog.cpp48
-rw-r--r--dom/media/webrtc/transport/transportlayerlog.h38
-rw-r--r--dom/media/webrtc/transport/transportlayerloopback.cpp119
-rw-r--r--dom/media/webrtc/transport/transportlayerloopback.h109
-rw-r--r--dom/media/webrtc/transport/transportlayersrtp.cpp222
-rw-r--r--dom/media/webrtc/transport/transportlayersrtp.h43
-rw-r--r--dom/media/webrtc/transportbridge/MediaPipeline.cpp1682
-rw-r--r--dom/media/webrtc/transportbridge/MediaPipeline.h425
-rw-r--r--dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp153
-rw-r--r--dom/media/webrtc/transportbridge/MediaPipelineFilter.h89
-rw-r--r--dom/media/webrtc/transportbridge/RtpLogger.cpp67
-rw-r--r--dom/media/webrtc/transportbridge/RtpLogger.h28
-rw-r--r--dom/media/webrtc/transportbridge/moz.build27
261 files changed, 67266 insertions, 0 deletions
diff --git a/dom/media/webrtc/transport/README b/dom/media/webrtc/transport/README
new file mode 100644
index 0000000000..3630b2aa1f
--- /dev/null
+++ b/dom/media/webrtc/transport/README
@@ -0,0 +1,45 @@
+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
+(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.
+* Loopback -- a loopback IO mechanism
+* Logging -- a passthrough that just logs its data
+
+The last two are primarily for debugging.
+
+
diff --git a/dom/media/webrtc/transport/SrtpFlow.cpp b/dom/media/webrtc/transport/SrtpFlow.cpp
new file mode 100644
index 0000000000..827d1a0f6d
--- /dev/null
+++ b/dom/media/webrtc/transport/SrtpFlow.cpp
@@ -0,0 +1,259 @@
+/* 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 "SrtpFlow.h"
+
+#include "srtp.h"
+
+#include "transportlayerdtls.h"
+
+#include "mozilla/RefPtr.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+bool SrtpFlow::initialized; // Static
+
+SrtpFlow::~SrtpFlow() {
+ if (session_) {
+ srtp_dealloc(session_);
+ }
+}
+
+unsigned int SrtpFlow::KeySize(int cipher_suite) {
+ srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
+ return srtp_profile_get_master_key_length(profile);
+}
+
+unsigned int SrtpFlow::SaltSize(int cipher_suite) {
+ srtp_profile_t profile = static_cast<srtp_profile_t>(cipher_suite);
+ return srtp_profile_get_master_salt_length(profile);
+}
+
+RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite, bool inbound,
+ const void* key, size_t key_len) {
+ nsresult res = Init();
+ if (!NS_SUCCEEDED(res)) return nullptr;
+
+ RefPtr<SrtpFlow> flow = new SrtpFlow();
+
+ if (!key) {
+ MOZ_MTLOG(ML_ERROR, "Null SRTP key specified");
+ return nullptr;
+ }
+
+ if ((key_len > SRTP_MAX_KEY_LENGTH) || (key_len < SRTP_MIN_KEY_LENGTH)) {
+ MOZ_ASSERT(false, "Invalid SRTP key length");
+ return nullptr;
+ }
+
+ srtp_policy_t policy;
+ memset(&policy, 0, sizeof(srtp_policy_t));
+
+ // Note that we set the same cipher suite for RTP and RTCP
+ // since any flow can only have one cipher suite with DTLS-SRTP
+ switch (cipher_suite) {
+ case kDtlsSrtpAeadAes256Gcm:
+ MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AEAD_AES_256_GCM");
+ srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtp);
+ srtp_crypto_policy_set_aes_gcm_256_16_auth(&policy.rtcp);
+ break;
+ case kDtlsSrtpAeadAes128Gcm:
+ MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AEAD_AES_128_GCM");
+ srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtp);
+ srtp_crypto_policy_set_aes_gcm_128_16_auth(&policy.rtcp);
+ break;
+ case kDtlsSrtpAes128CmHmacSha1_80:
+ MOZ_MTLOG(ML_DEBUG,
+ "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80");
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
+ break;
+ case kDtlsSrtpAes128CmHmacSha1_32:
+ MOZ_MTLOG(ML_DEBUG,
+ "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32");
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(
+ &policy.rtcp); // 80-bit per RFC 5764
+ break; // S 4.1.2.
+ default:
+ MOZ_MTLOG(ML_ERROR, "Request to set unknown SRTP cipher suite");
+ return nullptr;
+ }
+ // This key is copied into the srtp_t object, so we don't
+ // need to keep it.
+ policy.key =
+ const_cast<unsigned char*>(static_cast<const unsigned char*>(key));
+ policy.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound;
+ policy.ssrc.value = 0;
+ policy.window_size =
+ 1024; // Use the Chrome value. Needs to be revisited. Default is 128
+ policy.allow_repeat_tx = 1; // Use Chrome value; needed for NACK mode to work
+ policy.next = nullptr;
+
+ // Now make the session
+ srtp_err_status_t r = srtp_create(&flow->session_, &policy);
+ if (r != srtp_err_status_ok) {
+ MOZ_MTLOG(ML_ERROR, "Error creating srtp session");
+ return nullptr;
+ }
+
+ return flow;
+}
+
+nsresult SrtpFlow::CheckInputs(bool protect, void* in, int in_len, int max_len,
+ int* out_len) {
+ MOZ_ASSERT(in);
+ if (!in) {
+ MOZ_MTLOG(ML_ERROR, "NULL input value");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (in_len < 0) {
+ MOZ_MTLOG(ML_ERROR, "Input length is negative");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (max_len < 0) {
+ MOZ_MTLOG(ML_ERROR, "Max output length is negative");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (protect) {
+ if ((max_len < SRTP_MAX_EXPANSION) ||
+ ((max_len - SRTP_MAX_EXPANSION) < in_len)) {
+ MOZ_MTLOG(ML_ERROR, "Output too short");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ } else {
+ if (in_len > max_len) {
+ MOZ_MTLOG(ML_ERROR, "Output too short");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult SrtpFlow::ProtectRtp(void* in, int in_len, int max_len, int* out_len) {
+ nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
+ if (NS_FAILED(res)) return res;
+
+ int len = in_len;
+ srtp_err_status_t r = srtp_protect(session_, in, &len);
+
+ if (r != srtp_err_status_ok) {
+ MOZ_MTLOG(ML_ERROR, "Error protecting SRTP packet");
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_ASSERT(len <= max_len);
+ *out_len = len;
+
+ MOZ_MTLOG(ML_DEBUG,
+ "Successfully protected an SRTP packet of len " << *out_len);
+
+ return NS_OK;
+}
+
+nsresult SrtpFlow::UnprotectRtp(void* in, int in_len, int max_len,
+ int* out_len) {
+ nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
+ if (NS_FAILED(res)) return res;
+
+ int len = in_len;
+ srtp_err_status_t r = srtp_unprotect(session_, in, &len);
+
+ if (r != srtp_err_status_ok) {
+ MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTP packet error=" << (int)r);
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_ASSERT(len <= max_len);
+ *out_len = len;
+
+ MOZ_MTLOG(ML_DEBUG,
+ "Successfully unprotected an SRTP packet of len " << *out_len);
+
+ return NS_OK;
+}
+
+nsresult SrtpFlow::ProtectRtcp(void* in, int in_len, int max_len,
+ int* out_len) {
+ nsresult res = CheckInputs(true, in, in_len, max_len, out_len);
+ if (NS_FAILED(res)) return res;
+
+ int len = in_len;
+ srtp_err_status_t r = srtp_protect_rtcp(session_, in, &len);
+
+ if (r != srtp_err_status_ok) {
+ MOZ_MTLOG(ML_ERROR, "Error protecting SRTCP packet");
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_ASSERT(len <= max_len);
+ *out_len = len;
+
+ MOZ_MTLOG(ML_DEBUG,
+ "Successfully protected an SRTCP packet of len " << *out_len);
+
+ return NS_OK;
+}
+
+nsresult SrtpFlow::UnprotectRtcp(void* in, int in_len, int max_len,
+ int* out_len) {
+ nsresult res = CheckInputs(false, in, in_len, max_len, out_len);
+ if (NS_FAILED(res)) return res;
+
+ int len = in_len;
+ srtp_err_status_t r = srtp_unprotect_rtcp(session_, in, &len);
+
+ if (r != srtp_err_status_ok) {
+ MOZ_MTLOG(ML_ERROR, "Error unprotecting SRTCP packet error=" << (int)r);
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_ASSERT(len <= max_len);
+ *out_len = len;
+
+ MOZ_MTLOG(ML_DEBUG,
+ "Successfully unprotected an SRTCP packet of len " << *out_len);
+
+ return NS_OK;
+}
+
+// Statics
+void SrtpFlow::srtp_event_handler(srtp_event_data_t* data) {
+ // TODO(ekr@rtfm.com): Implement this
+ MOZ_CRASH();
+}
+
+nsresult SrtpFlow::Init() {
+ if (!initialized) {
+ srtp_err_status_t r = srtp_init();
+ if (r != srtp_err_status_ok) {
+ MOZ_MTLOG(ML_ERROR, "Could not initialize SRTP");
+ MOZ_ASSERT(PR_FALSE);
+ return NS_ERROR_FAILURE;
+ }
+
+ r = srtp_install_event_handler(&SrtpFlow::srtp_event_handler);
+ if (r != srtp_err_status_ok) {
+ MOZ_MTLOG(ML_ERROR, "Could not install SRTP event handler");
+ MOZ_ASSERT(PR_FALSE);
+ return NS_ERROR_FAILURE;
+ }
+
+ initialized = true;
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/SrtpFlow.h b/dom/media/webrtc/transport/SrtpFlow.h
new file mode 100644
index 0000000000..92fbfcf1a5
--- /dev/null
+++ b/dom/media/webrtc/transport/SrtpFlow.h
@@ -0,0 +1,69 @@
+/* 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 srtpflow_h__
+#define srtpflow_h__
+
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+#include "srtp.h"
+
+namespace mozilla {
+
+#define SRTP_ICM_MASTER_KEY_LENGTH 16
+#define SRTP_ICM_MASTER_SALT_LENGTH 14
+#define SRTP_ICM_MAX_MASTER_LENGTH \
+ (SRTP_ICM_MASTER_KEY_LENGTH + SRTP_ICM_MASTER_SALT_LENGTH)
+
+#define SRTP_GCM_MASTER_KEY_MIN_LENGTH 16
+#define SRTP_GCM_MASTER_KEY_MAX_LENGTH 32
+#define SRTP_GCM_MASTER_SALT_LENGTH 12
+
+#define SRTP_GCM_MIN_MASTER_LENGTH \
+ (SRTP_GCM_MASTER_KEY_MIN_LENGTH + SRTP_GCM_MASTER_SALT_LENGTH)
+#define SRTP_GCM_MAX_MASTER_LENGTH \
+ (SRTP_GCM_MASTER_KEY_MAX_LENGTH + SRTP_GCM_MASTER_SALT_LENGTH)
+
+#define SRTP_MIN_KEY_LENGTH SRTP_GCM_MIN_MASTER_LENGTH
+#define SRTP_MAX_KEY_LENGTH SRTP_GCM_MAX_MASTER_LENGTH
+
+// SRTCP requires an auth tag *plus* a 4-byte index-plus-'E'-bit value (see
+// RFC 3711)
+#define SRTP_MAX_EXPANSION (SRTP_MAX_TRAILER_LEN + 4)
+
+class SrtpFlow {
+ ~SrtpFlow();
+
+ public:
+ static unsigned int KeySize(int cipher_suite);
+ static unsigned int SaltSize(int cipher_suite);
+
+ static RefPtr<SrtpFlow> Create(int cipher_suite, bool inbound,
+ const void* key, size_t key_len);
+
+ nsresult ProtectRtp(void* in, int in_len, int max_len, int* out_len);
+ nsresult UnprotectRtp(void* in, int in_len, int max_len, int* out_len);
+ nsresult ProtectRtcp(void* in, int in_len, int max_len, int* out_len);
+ nsresult UnprotectRtcp(void* in, int in_len, int max_len, int* out_len);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SrtpFlow)
+
+ static void srtp_event_handler(srtp_event_data_t* data);
+
+ private:
+ SrtpFlow() : session_(nullptr) {}
+
+ nsresult CheckInputs(bool protect, void* in, int in_len, int max_len,
+ int* out_len);
+
+ static nsresult Init();
+ static bool initialized; // Was libsrtp initialized? Only happens once.
+
+ srtp_t session_;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.cpp b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.cpp
new file mode 100644
index 0000000000..c126e6b791
--- /dev/null
+++ b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocketWrapper.h"
+
+#include "mozilla/net/WebrtcTCPSocketChild.h"
+
+#include "nsNetCID.h"
+#include "nsProxyRelease.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nr_socket_proxy_config.h"
+
+namespace mozilla::net {
+
+using std::shared_ptr;
+
+WebrtcTCPSocketWrapper::WebrtcTCPSocketWrapper(
+ WebrtcTCPSocketCallback* aCallbacks)
+ : mProxyCallbacks(aCallbacks),
+ mWebrtcTCPSocket(nullptr),
+ mMainThread(nullptr),
+ mSocketThread(nullptr) {
+ mMainThread = GetMainThreadEventTarget();
+ mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ MOZ_RELEASE_ASSERT(mMainThread, "no main thread");
+ MOZ_RELEASE_ASSERT(mSocketThread, "no socket thread");
+}
+
+WebrtcTCPSocketWrapper::~WebrtcTCPSocketWrapper() {
+ MOZ_ASSERT(!mWebrtcTCPSocket, "webrtc TCP socket non-null");
+
+ // If we're never opened then we never get an OnClose from our parent process.
+ // We need to release our callbacks here safely.
+ NS_ProxyRelease("WebrtcTCPSocketWrapper::CleanUpCallbacks", mSocketThread,
+ mProxyCallbacks.forget());
+}
+
+void WebrtcTCPSocketWrapper::AsyncOpen(
+ const nsCString& aHost, const int& aPort, const nsCString& aLocalAddress,
+ const int& aLocalPort, bool aUseTls,
+ const shared_ptr<NrSocketProxyConfig>& aConfig) {
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch(
+ NewRunnableMethod<const nsCString, const int, const nsCString,
+ const int, bool,
+ const shared_ptr<NrSocketProxyConfig>>(
+ "WebrtcTCPSocketWrapper::AsyncOpen", this,
+ &WebrtcTCPSocketWrapper::AsyncOpen, aHost, aPort, aLocalAddress,
+ aLocalPort, aUseTls, aConfig)));
+ return;
+ }
+
+ MOZ_ASSERT(!mWebrtcTCPSocket, "wrapper already open");
+ mWebrtcTCPSocket = new WebrtcTCPSocketChild(this);
+ mWebrtcTCPSocket->AsyncOpen(aHost, aPort, aLocalAddress, aLocalPort, aUseTls,
+ aConfig);
+}
+
+void WebrtcTCPSocketWrapper::SendWrite(nsTArray<uint8_t>&& aReadData) {
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(
+ mMainThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>(
+ "WebrtcTCPSocketWrapper::SendWrite", this,
+ &WebrtcTCPSocketWrapper::SendWrite, std::move(aReadData))));
+ return;
+ }
+
+ MOZ_ASSERT(mWebrtcTCPSocket, "webrtc TCP socket should be non-null");
+ mWebrtcTCPSocket->SendWrite(aReadData);
+}
+
+void WebrtcTCPSocketWrapper::Close() {
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch(
+ NewRunnableMethod("WebrtcTCPSocketWrapper::Close", this,
+ &WebrtcTCPSocketWrapper::Close)));
+ return;
+ }
+
+ // We're only open if we have a channel. Also Close() should be idempotent.
+ if (mWebrtcTCPSocket) {
+ RefPtr<WebrtcTCPSocketChild> child = mWebrtcTCPSocket;
+ mWebrtcTCPSocket = nullptr;
+ child->SendClose();
+ }
+}
+
+void WebrtcTCPSocketWrapper::OnClose(nsresult aReason) {
+ MOZ_ASSERT(NS_IsMainThread(), "not on main thread");
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+
+ MOZ_ALWAYS_SUCCEEDS(mSocketThread->Dispatch(NewRunnableMethod<nsresult>(
+ "WebrtcTCPSocketWrapper::OnClose", mProxyCallbacks,
+ &WebrtcTCPSocketCallback::OnClose, aReason)));
+
+ NS_ProxyRelease("WebrtcTCPSocketWrapper::CleanUpCallbacks", mSocketThread,
+ mProxyCallbacks.forget());
+}
+
+void WebrtcTCPSocketWrapper::OnRead(nsTArray<uint8_t>&& aReadData) {
+ MOZ_ASSERT(NS_IsMainThread(), "not on main thread");
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+
+ MOZ_ALWAYS_SUCCEEDS(
+ mSocketThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>(
+ "WebrtcTCPSocketWrapper::OnRead", mProxyCallbacks,
+ &WebrtcTCPSocketCallback::OnRead, std::move(aReadData))));
+}
+
+void WebrtcTCPSocketWrapper::OnConnected(const nsACString& aProxyType) {
+ MOZ_ASSERT(NS_IsMainThread(), "not on main thread");
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+
+ MOZ_ALWAYS_SUCCEEDS(mSocketThread->Dispatch(NewRunnableMethod<nsCString>(
+ "WebrtcTCPSocketWrapper::OnConnected", mProxyCallbacks,
+ &WebrtcTCPSocketCallback::OnConnected, aProxyType)));
+}
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.h b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.h
new file mode 100644
index 0000000000..c0775ee9aa
--- /dev/null
+++ b/dom/media/webrtc/transport/WebrtcTCPSocketWrapper.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 webrtc_tcp_socket_wrapper__
+#define webrtc_tcp_socket_wrapper__
+
+#include <memory>
+
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+#include "mozilla/net/WebrtcTCPSocketCallback.h"
+
+class nsIEventTarget;
+
+namespace mozilla {
+
+class NrSocketProxyConfig;
+
+namespace net {
+
+class WebrtcTCPSocketChild;
+
+/**
+ * WebrtcTCPSocketWrapper is a protector class for mtransport and IPDL.
+ * mtransport and IPDL cannot include headers from each other due to conflicting
+ * typedefs. Also it helps users by dispatching calls to the appropriate thread
+ * based on mtransport's and IPDL's threading requirements.
+ *
+ * WebrtcTCPSocketWrapper is only used in the child process.
+ * WebrtcTCPSocketWrapper does not dispatch for the parent process.
+ * WebrtcTCPSocketCallback calls are dispatched to the STS thread.
+ * IPDL calls are dispatched to the main thread.
+ */
+class WebrtcTCPSocketWrapper : public WebrtcTCPSocketCallback {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketWrapper, override)
+
+ explicit WebrtcTCPSocketWrapper(WebrtcTCPSocketCallback* aCallbacks);
+
+ virtual void AsyncOpen(const nsCString& aHost, const int& aPort,
+ const nsCString& aLocalAddress, const int& aLocalPort,
+ bool aUseTls,
+ const std::shared_ptr<NrSocketProxyConfig>& aConfig);
+ virtual void SendWrite(nsTArray<uint8_t>&& aReadData);
+ virtual void Close();
+
+ // WebrtcTCPSocketCallback
+ virtual void OnClose(nsresult aReason) override;
+ virtual void OnConnected(const nsACString& aProxyType) override;
+ virtual void OnRead(nsTArray<uint8_t>&& aReadData) override;
+
+ protected:
+ RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks;
+ RefPtr<WebrtcTCPSocketChild> mWebrtcTCPSocket;
+
+ nsCOMPtr<nsIEventTarget> mMainThread;
+ nsCOMPtr<nsIEventTarget> mSocketThread;
+
+ virtual ~WebrtcTCPSocketWrapper();
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // webrtc_tcp_socket_wrapper__
diff --git a/dom/media/webrtc/transport/build/moz.build b/dom/media/webrtc/transport/build/moz.build
new file mode 100644
index 0000000000..7349ee5d71
--- /dev/null
+++ b/dom/media/webrtc/transport/build/moz.build
@@ -0,0 +1,44 @@
+# -*- 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.transport += [
+ "../dtlsidentity.h",
+ "../m_cpp_utils.h",
+ "../mediapacket.h",
+ "../nr_socket_proxy_config.h",
+ "../nricectx.h",
+ "../nricemediastream.h",
+ "../nriceresolverfake.h",
+ "../nricestunaddr.h",
+ "../rlogconnector.h",
+ "../runnable_utils.h",
+ "../sigslot.h",
+ "../simpletokenbucket.h",
+ "../SrtpFlow.h",
+ "../stun_socket_filter.h",
+ "../transportflow.h",
+ "../transportlayer.h",
+ "../transportlayerdtls.h",
+ "../transportlayerice.h",
+ "../transportlayerlog.h",
+ "../transportlayerloopback.h",
+ "../transportlayersrtp.h",
+]
+
+include("../common.build")
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-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, nrappkit/IPDL typedef conflicts in
+# PBrowserOrId and WebrtcTCPSocketChild.
+SOURCES += transport_cppsrcs
+
+FINAL_LIBRARY = "xul"
diff --git a/dom/media/webrtc/transport/common.build b/dom/media/webrtc/transport/common.build
new file mode 100644
index 0000000000..b907b57ee8
--- /dev/null
+++ b/dom/media/webrtc/transport/common.build
@@ -0,0 +1,94 @@
+# -*- 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/.
+
+transport_lcppsrcs = [
+ "dtlsidentity.cpp",
+ "mediapacket.cpp",
+ "nr_socket_proxy_config.cpp",
+ "nr_socket_prsock.cpp",
+ "nr_socket_tcp.cpp",
+ "nr_timer.cpp",
+ "nricectx.cpp",
+ "nricemediastream.cpp",
+ "nriceresolver.cpp",
+ "nriceresolverfake.cpp",
+ "nricestunaddr.cpp",
+ "nrinterfaceprioritizer.cpp",
+ "rlogconnector.cpp",
+ "simpletokenbucket.cpp",
+ "SrtpFlow.cpp",
+ "stun_socket_filter.cpp",
+ "test_nr_socket.cpp",
+ "transportflow.cpp",
+ "transportlayer.cpp",
+ "transportlayerdtls.cpp",
+ "transportlayerice.cpp",
+ "transportlayerlog.cpp",
+ "transportlayerloopback.cpp",
+ "transportlayersrtp.cpp",
+ "WebrtcTCPSocketWrapper.cpp",
+]
+
+transport_cppsrcs = [
+ "/dom/media/webrtc/transport/%s" % s for s in sorted(transport_lcppsrcs)
+]
+
+LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/",
+ "/dom/media/webrtc/transport/third_party/",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/crypto",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/ice",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/net",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/stun",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/util",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/event",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/log",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/plugin",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/registry",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/share",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/stats",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr",
+ "/third_party/libsrtp/src/crypto/include",
+ "/third_party/libsrtp/src/include",
+]
+
+if CONFIG["OS_TARGET"] in ["Darwin", "DragonFly", "FreeBSD", "NetBSD", "OpenBSD"]:
+ if CONFIG["OS_TARGET"] == "Darwin":
+ DEFINES["DARWIN"] = True
+ else:
+ DEFINES["BSD"] = True
+ LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include",
+ ]
+elif CONFIG["OS_TARGET"] == "Linux":
+ DEFINES["LINUX"] = True
+ LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include",
+ ]
+elif CONFIG["OS_TARGET"] == "Android":
+ DEFINES["LINUX"] = True
+ DEFINES["ANDROID"] = True
+ LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include",
+ ]
+elif CONFIG["OS_TARGET"] == "WINNT":
+ DEFINES["WIN"] = True
+ # for stun.h
+ DEFINES["WIN32"] = True
+ LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/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"
diff --git a/dom/media/webrtc/transport/dtlsidentity.cpp b/dom/media/webrtc/transport/dtlsidentity.cpp
new file mode 100644
index 0000000000..2e08c04b60
--- /dev/null
+++ b/dom/media/webrtc/transport/dtlsidentity.cpp
@@ -0,0 +1,324 @@
+/* -*- 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 "mozpkix/nss_scoped_ptrs.h"
+#include "secerr.h"
+#include "sslerr.h"
+
+#include "mozilla/Sprintf.h"
+
+namespace mozilla {
+
+SECItem* WrapPrivateKeyInfoWithEmptyPassword(
+ SECKEYPrivateKey* pk) /* encrypt this private key */
+{
+ if (!pk) {
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return nullptr;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return nullptr;
+ }
+
+ // For private keys, NSS cannot export anything other than RSA, but we need EC
+ // also. So, we use the private key encryption function to serialize instead,
+ // using a hard-coded dummy password; this is not intended to provide any
+ // additional security, it just works around a limitation in NSS.
+ SECItem dummyPassword = {siBuffer, nullptr, 0};
+ UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
+ slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr));
+
+ if (!epki) {
+ return nullptr;
+ }
+
+ return SEC_ASN1EncodeItem(
+ nullptr, nullptr, epki.get(),
+ NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false));
+}
+
+SECStatus UnwrapPrivateKeyInfoWithEmptyPassword(
+ SECItem* derPKI, const UniqueCERTCertificate& aCert,
+ SECKEYPrivateKey** privk) {
+ if (!derPKI || !aCert || !privk) {
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return SECFailure;
+ }
+
+ UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get()));
+ // This is a pointer to data inside publicKey
+ SECItem* publicValue = nullptr;
+ switch (publicKey->keyType) {
+ case dsaKey:
+ publicValue = &publicKey->u.dsa.publicValue;
+ break;
+ case dhKey:
+ publicValue = &publicKey->u.dh.publicValue;
+ break;
+ case rsaKey:
+ publicValue = &publicKey->u.rsa.modulus;
+ break;
+ case ecKey:
+ publicValue = &publicKey->u.ec.publicValue;
+ break;
+ default:
+ MOZ_ASSERT(false);
+ PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
+ return SECFailure;
+ }
+
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return SECFailure;
+ }
+
+ UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!temparena) {
+ return SECFailure;
+ }
+
+ SECKEYEncryptedPrivateKeyInfo* epki =
+ PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo);
+ if (!epki) {
+ return SECFailure;
+ }
+
+ SECStatus rv = SEC_ASN1DecodeItem(
+ temparena.get(), epki,
+ NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI);
+ if (rv != SECSuccess) {
+ // If SEC_ASN1DecodeItem fails, we cannot assume anything about the
+ // validity of the data in epki. The best we can do is free the arena
+ // and return.
+ return rv;
+ }
+
+ // See comment in WrapPrivateKeyInfoWithEmptyPassword about this
+ // dummy password stuff.
+ SECItem dummyPassword = {siBuffer, nullptr, 0};
+ return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
+ slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false,
+ publicKey->keyType, KU_ALL, privk, nullptr);
+}
+
+nsresult DtlsIdentity::Serialize(nsTArray<uint8_t>* aKeyDer,
+ nsTArray<uint8_t>* aCertDer) {
+ ScopedSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(private_key_.get()));
+ if (!derPki) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aKeyDer->AppendElements(derPki->data, derPki->len);
+ aCertDer->AppendElements(cert_->derCert.data, cert_->derCert.len);
+ return NS_OK;
+}
+
+/* static */
+RefPtr<DtlsIdentity> DtlsIdentity::Deserialize(
+ const nsTArray<uint8_t>& aKeyDer, const nsTArray<uint8_t>& aCertDer,
+ SSLKEAType authType) {
+ SECItem certDer = {siBuffer, const_cast<uint8_t*>(aCertDer.Elements()),
+ static_cast<unsigned int>(aCertDer.Length())};
+ UniqueCERTCertificate cert(CERT_NewTempCertificate(
+ CERT_GetDefaultCertDB(), &certDer, nullptr, true, true));
+
+ SECItem derPKI = {siBuffer, const_cast<uint8_t*>(aKeyDer.Elements()),
+ static_cast<unsigned int>(aKeyDer.Length())};
+
+ SECKEYPrivateKey* privateKey;
+ if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) !=
+ SECSuccess) {
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ return new DtlsIdentity(UniqueSECKEYPrivateKey(privateKey), std::move(cert),
+ authType);
+}
+
+RefPtr<DtlsIdentity> DtlsIdentity::Generate() {
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return nullptr;
+ }
+
+ uint8_t random_name[16];
+
+ SECStatus rv =
+ PK11_GenerateRandomOnSlot(slot.get(), random_name, sizeof(random_name));
+ if (rv != SECSuccess) return nullptr;
+
+ std::string name;
+ char chunk[3];
+ for (unsigned char r_name : random_name) {
+ SprintfLiteral(chunk, "%.2x", r_name);
+ name += chunk;
+ }
+
+ std::string subject_name_string = "CN=" + name;
+ UniqueCERTName 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;
+
+ SECKEYPublicKey* pubkey;
+ UniqueSECKEYPrivateKey private_key(
+ PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsaParams,
+ &pubkey, PR_FALSE, PR_TRUE, nullptr));
+ if (private_key == nullptr) return nullptr;
+ UniqueSECKEYPublicKey public_key(pubkey);
+ pubkey = nullptr;
+
+ UniqueCERTSubjectPublicKeyInfo spki(
+ SECKEY_CreateSubjectPublicKeyInfo(public_key.get()));
+ if (!spki) {
+ return nullptr;
+ }
+
+ UniqueCERTCertificateRequest certreq(
+ CERT_CreateCertificateRequest(subject_name.get(), spki.get(), 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);
+
+ UniqueCERTValidity 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.get(), reinterpret_cast<unsigned char*>(&serial), sizeof(serial));
+ if (rv != SECSuccess) {
+ return nullptr;
+ }
+
+ UniqueCERTCertificate certificate(CERT_CreateCertificate(
+ serial, subject_name.get(), validity.get(), certreq.get()));
+ if (!certificate) {
+ return nullptr;
+ }
+
+ PLArenaPool* arena = certificate->arena;
+
+ rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
+ SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, nullptr);
+ 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.get(),
+ 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.get(),
+ SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
+ if (rv != SECSuccess) {
+ return nullptr;
+ }
+ certificate->derCert = *signedCert;
+
+ return new DtlsIdentity(std::move(private_key), std::move(certificate),
+ ssl_kea_ecdh);
+}
+
+const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256";
+
+nsresult DtlsIdentity::ComputeFingerprint(DtlsDigest* digest) const {
+ const UniqueCERTCertificate& c = cert();
+ MOZ_ASSERT(c);
+
+ return ComputeFingerprint(c, digest);
+}
+
+nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert,
+ DtlsDigest* digest) {
+ MOZ_ASSERT(cert);
+
+ HASH_HashType ht;
+
+ if (digest->algorithm_ == "sha-1") {
+ ht = HASH_AlgSHA1;
+ } else if (digest->algorithm_ == "sha-224") {
+ ht = HASH_AlgSHA224;
+ } else if (digest->algorithm_ == "sha-256") {
+ ht = HASH_AlgSHA256;
+ } else if (digest->algorithm_ == "sha-384") {
+ ht = HASH_AlgSHA384;
+ } else if (digest->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
+ digest->value_.resize(ho->length);
+
+ SECStatus rv = HASH_HashBuf(ho->type, digest->value_.data(),
+ cert->derCert.data, cert->derCert.len);
+ if (rv != SECSuccess) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/dtlsidentity.h b/dom/media/webrtc/transport/dtlsidentity.h
new file mode 100644
index 0000000000..b4f7686618
--- /dev/null
+++ b/dom/media/webrtc/transport/dtlsidentity.h
@@ -0,0 +1,101 @@
+/* -*- 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 <utility>
+#include <vector>
+
+#include "ScopedNSSTypes.h"
+#include "m_cpp_utils.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+#include "sslt.h"
+
+// All code in this module requires NSS to be live.
+// Callers must initialize NSS and implement the nsNSSShutdownObject
+// protocol.
+namespace mozilla {
+
+class DtlsDigest {
+ public:
+ const static size_t kMaxDtlsDigestLength = HASH_LENGTH_MAX;
+ DtlsDigest() = default;
+ explicit DtlsDigest(const std::string& algorithm) : algorithm_(algorithm) {}
+ DtlsDigest(const std::string& algorithm, const std::vector<uint8_t>& value)
+ : algorithm_(algorithm), value_(value) {
+ MOZ_ASSERT(value.size() <= kMaxDtlsDigestLength);
+ }
+ ~DtlsDigest() = default;
+
+ bool operator!=(const DtlsDigest& rhs) const { return !operator==(rhs); }
+
+ bool operator==(const DtlsDigest& rhs) const {
+ if (algorithm_ != rhs.algorithm_) {
+ return false;
+ }
+
+ return value_ == rhs.value_;
+ }
+
+ std::string algorithm_;
+ std::vector<uint8_t> value_;
+};
+
+typedef std::vector<DtlsDigest> DtlsDigestList;
+
+class DtlsIdentity final {
+ public:
+ // This constructor takes ownership of privkey and cert.
+ DtlsIdentity(UniqueSECKEYPrivateKey privkey, UniqueCERTCertificate cert,
+ SSLKEAType authType)
+ : private_key_(std::move(privkey)),
+ cert_(std::move(cert)),
+ auth_type_(authType) {}
+
+ // Allows serialization/deserialization; cannot write IPC serialization code
+ // directly for DtlsIdentity, since IPC-able types need to be constructable
+ // on the stack.
+ nsresult Serialize(nsTArray<uint8_t>* aKeyDer, nsTArray<uint8_t>* aCertDer);
+ static RefPtr<DtlsIdentity> Deserialize(const nsTArray<uint8_t>& aKeyDer,
+ const nsTArray<uint8_t>& aCertDer,
+ SSLKEAType 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_; }
+ const UniqueSECKEYPrivateKey& 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(DtlsDigest* digest) const;
+ static nsresult ComputeFingerprint(const UniqueCERTCertificate& cert,
+ DtlsDigest* digest);
+
+ static const std::string DEFAULT_HASH_ALGORITHM;
+ enum { HASH_ALGORITHM_MAX_LENGTH = 64 };
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DtlsIdentity)
+
+ private:
+ ~DtlsIdentity() = default;
+ DISALLOW_COPY_ASSIGN(DtlsIdentity);
+
+ UniqueSECKEYPrivateKey private_key_;
+ UniqueCERTCertificate cert_;
+ SSLKEAType auth_type_;
+};
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/fuzztest/moz.build b/dom/media/webrtc/transport/fuzztest/moz.build
new file mode 100644
index 0000000000..f22a5a702b
--- /dev/null
+++ b/dom/media/webrtc/transport/fuzztest/moz.build
@@ -0,0 +1,31 @@
+# -*- 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/.
+
+Library("FuzzingStun")
+
+DEFINES["HAVE_STRDUP"] = True
+
+LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/third_party/nICEr/src/net",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/stun",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/event",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/log",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/plugin",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/share",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/stats",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr",
+ "/ipc/chromium/src",
+]
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
+
+SOURCES += [
+ "stun_parser_libfuzz.cpp",
+]
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/dom/media/webrtc/transport/fuzztest/stun_parser_libfuzz.cpp b/dom/media/webrtc/transport/fuzztest/stun_parser_libfuzz.cpp
new file mode 100644
index 0000000000..73bae5024c
--- /dev/null
+++ b/dom/media/webrtc/transport/fuzztest/stun_parser_libfuzz.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 <string>
+
+#include "gtest/gtest.h"
+
+#include "FuzzingInterface.h"
+
+extern "C" {
+#include <csi_platform.h>
+#include "stun_msg.h"
+#include "stun_codec.h"
+}
+
+int FuzzingInitStunParser(int* argc, char*** argv) { return 0; }
+
+static int RunStunParserFuzzing(const uint8_t* data, size_t size) {
+ nr_stun_message* req = 0;
+
+ UCHAR* mes = (UCHAR*)data;
+
+ if (!nr_stun_message_create2(&req, mes, size)) {
+ nr_stun_decode_message(req, nullptr, nullptr);
+ nr_stun_message_destroy(&req);
+ }
+
+ return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitStunParser, RunStunParserFuzzing,
+ StunParser);
diff --git a/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h b/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h
new file mode 100644
index 0000000000..34ec46726e
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h
@@ -0,0 +1,54 @@
+/* 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 mozilla_net_NrIceStunAddrMessageUtils_h
+#define mozilla_net_NrIceStunAddrMessageUtils_h
+
+// forward declare NrIceStunAddr for --disable-webrtc builds where
+// the header will not be available.
+namespace mozilla {
+class NrIceStunAddr;
+} // namespace mozilla
+
+#include "ipc/IPCMessageUtils.h"
+#ifdef MOZ_WEBRTC
+# include "transport/nricestunaddr.h"
+#endif
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::NrIceStunAddr> {
+ static void Write(MessageWriter* aWriter,
+ const mozilla::NrIceStunAddr& aParam) {
+#ifdef MOZ_WEBRTC
+ const size_t bufSize = aParam.SerializationBufferSize();
+ char* buffer = new char[bufSize];
+ aParam.Serialize(buffer, bufSize);
+ aWriter->WriteBytes((void*)buffer, bufSize);
+ delete[] buffer;
+#endif
+ }
+
+ static bool Read(MessageReader* aReader, mozilla::NrIceStunAddr* aResult) {
+#ifdef MOZ_WEBRTC
+ const size_t bufSize = aResult->SerializationBufferSize();
+ char* buffer = new char[bufSize];
+ bool result = aReader->ReadBytesInto((void*)buffer, bufSize);
+
+ if (result) {
+ result = result && (NS_OK == aResult->Deserialize(buffer, bufSize));
+ }
+ delete[] buffer;
+
+ return result;
+#else
+ return false;
+#endif
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_net_NrIceStunAddrMessageUtils_h
diff --git a/dom/media/webrtc/transport/ipc/PStunAddrsParams.h b/dom/media/webrtc/transport/ipc/PStunAddrsParams.h
new file mode 100644
index 0000000000..315925609d
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/PStunAddrsParams.h
@@ -0,0 +1,33 @@
+/* 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 PStunAddrsParams_h
+#define PStunAddrsParams_h
+
+#include "mozilla/Maybe.h"
+#include "nsTArray.h"
+
+#ifdef MOZ_WEBRTC
+# include "transport/nricestunaddr.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+// Need to define typedef in .h file--can't seem to in ipdl.h file?
+#ifdef MOZ_WEBRTC
+typedef nsTArray<NrIceStunAddr> NrIceStunAddrArray;
+#else
+// a "dummy" typedef for --disabled-webrtc builds when the definition
+// for NrIceStunAddr is not available (otherwise we get complaints
+// about missing definitions for contructor and destructor)
+typedef nsTArray<int> NrIceStunAddrArray;
+#endif
+
+typedef Maybe<nsCString> MaybeNsCString;
+
+} // namespace net
+} // namespace mozilla
+
+#endif // PStunAddrsParams_h
diff --git a/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl b/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl
new file mode 100644
index 0000000000..f9096c8dee
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl
@@ -0,0 +1,35 @@
+/* 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 protocol PNecko;
+
+using NrIceStunAddrArray from "mozilla/net/PStunAddrsParams.h";
+using MaybeNsCString from "mozilla/net/PStunAddrsParams.h";
+
+include "mozilla/net/NrIceStunAddrMessageUtils.h";
+
+namespace mozilla {
+namespace net {
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+async protocol PStunAddrsRequest
+{
+ manager PNecko;
+
+parent:
+ async GetStunAddrs();
+
+ async RegisterMDNSHostname(nsCString hostname, nsCString address);
+ async QueryMDNSHostname(nsCString hostname);
+ async UnregisterMDNSHostname(nsCString hostname);
+
+ async __delete__();
+
+child:
+ async OnMDNSQueryComplete(nsCString hostname, MaybeNsCString address);
+ async OnStunAddrsAvailable(NrIceStunAddrArray iceStunAddrs);
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl b/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl
new file mode 100644
index 0000000000..3bc2a89828
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+include protocol PSocketProcess;
+
+include WebrtcProxyConfig;
+
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+
+namespace mozilla {
+namespace net {
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+async protocol PWebrtcTCPSocket
+{
+ manager PNecko or PSocketProcess;
+
+parent:
+ async AsyncOpen(nsCString aHost,
+ int32_t aPort,
+ nsCString aLocalAddress,
+ int32_t aLocalPort,
+ bool aUseTls,
+ WebrtcProxyConfig? aProxyConfig);
+ async Write(uint8_t[] aWriteData);
+ async Close();
+
+child:
+ async OnClose(nsresult aReason);
+ async OnConnected(nsCString aProxyType);
+ async OnRead(uint8_t[] aReadData);
+
+ async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp
new file mode 100644
index 0000000000..057fcbd330
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp
@@ -0,0 +1,45 @@
+/* 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 "StunAddrsRequestChild.h"
+
+#include "mozilla/net/NeckoChild.h"
+#include "nsISerialEventTarget.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+StunAddrsRequestChild::StunAddrsRequestChild(StunAddrsListener* listener)
+ : mListener(listener) {
+ gNeckoChild->SendPStunAddrsRequestConstructor(this);
+ // IPDL holds a reference until IPDL channel gets destroyed
+ AddIPDLReference();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestChild::RecvOnMDNSQueryComplete(
+ const nsACString& hostname, const Maybe<nsCString>& address) {
+ if (mListener) {
+ mListener->OnMDNSQueryComplete(PromiseFlatCString(hostname), address);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestChild::RecvOnStunAddrsAvailable(
+ const NrIceStunAddrArray& addrs) {
+ if (mListener) {
+ mListener->OnStunAddrsAvailable(addrs);
+ }
+ return IPC_OK();
+}
+
+void StunAddrsRequestChild::Cancel() { mListener = nullptr; }
+
+NS_IMPL_ADDREF(StunAddrsRequestChild)
+NS_IMPL_RELEASE(StunAddrsRequestChild)
+
+NS_IMPL_ADDREF(StunAddrsListener)
+NS_IMPL_RELEASE(StunAddrsListener)
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h
new file mode 100644
index 0000000000..f487f52baf
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h
@@ -0,0 +1,64 @@
+/* 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 mozilla_net_StunAddrsRequestChild_h
+#define mozilla_net_StunAddrsRequestChild_h
+
+#include "mozilla/net/PStunAddrsRequestChild.h"
+
+class nsISerialEventTarget;
+
+namespace mozilla::net {
+
+class StunAddrsListener {
+ public:
+ virtual void OnMDNSQueryComplete(const nsCString& hostname,
+ const Maybe<nsCString>& address) = 0;
+ virtual void OnStunAddrsAvailable(const NrIceStunAddrArray& addrs) = 0;
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ protected:
+ virtual ~StunAddrsListener() = default;
+
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+};
+
+class StunAddrsRequestChild final : public PStunAddrsRequestChild {
+ friend class PStunAddrsRequestChild;
+
+ public:
+ explicit StunAddrsRequestChild(StunAddrsListener* listener);
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ // Not sure why AddIPDLReference & ReleaseIPDLReference don't come
+ // from PStunAddrsRequestChild since the IPC plumbing seem to
+ // expect this.
+ void AddIPDLReference() { AddRef(); }
+ void ReleaseIPDLReference() { Release(); }
+
+ void Cancel();
+
+ protected:
+ virtual ~StunAddrsRequestChild() = default;
+
+ virtual mozilla::ipc::IPCResult RecvOnMDNSQueryComplete(
+ const nsACString& aHostname, const Maybe<nsCString>& aAddress) override;
+
+ virtual mozilla::ipc::IPCResult RecvOnStunAddrsAvailable(
+ const NrIceStunAddrArray& addrs) override;
+
+ RefPtr<StunAddrsListener> mListener;
+
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_StunAddrsRequestChild_h
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp
new file mode 100644
index 0000000000..23ef6dea73
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp
@@ -0,0 +1,262 @@
+/* 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 "StunAddrsRequestParent.h"
+
+#include "../runnable_utils.h"
+#include "mozilla/StaticPtr.h"
+#include "nsIThread.h"
+#include "nsNetUtil.h"
+
+#include "transport/nricectx.h"
+#include "transport/nricemediastream.h" // needed only for including nricectx.h
+#include "transport/nricestunaddr.h"
+
+#include "../mdns_service/mdns_service.h"
+
+extern "C" {
+#include "local_addr.h"
+}
+
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+static void mdns_service_resolved(void* cb, const char* hostname,
+ const char* addr) {
+ StunAddrsRequestParent* self = static_cast<StunAddrsRequestParent*>(cb);
+ self->OnQueryComplete(nsCString(hostname), Some(nsCString(addr)));
+}
+
+void mdns_service_timedout(void* cb, const char* hostname) {
+ StunAddrsRequestParent* self = static_cast<StunAddrsRequestParent*>(cb);
+ self->OnQueryComplete(nsCString(hostname), Nothing());
+}
+
+StunAddrsRequestParent::StunAddrsRequestParent() : mIPCClosed(false) {
+ NS_GetMainThread(getter_AddRefs(mMainThread));
+
+ nsresult res;
+ mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
+ MOZ_ASSERT(mSTSThread);
+}
+
+StunAddrsRequestParent::~StunAddrsRequestParent() {
+ ASSERT_ON_THREAD(mMainThread);
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvGetStunAddrs() {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ RUN_ON_THREAD(mSTSThread,
+ WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
+ &StunAddrsRequestParent::GetStunAddrs_s),
+ NS_DISPATCH_NORMAL);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvRegisterMDNSHostname(
+ const nsACString& aHostname, const nsACString& aAddress) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ if (mSharedMDNSService) {
+ mSharedMDNSService->RegisterHostname(aHostname.BeginReading(),
+ aAddress.BeginReading());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvQueryMDNSHostname(
+ const nsACString& aHostname) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ if (mSharedMDNSService) {
+ mSharedMDNSService->QueryHostname(this, aHostname.BeginReading());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvUnregisterMDNSHostname(
+ const nsACString& aHostname) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ if (mSharedMDNSService) {
+ mSharedMDNSService->UnregisterHostname(aHostname.BeginReading());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::Recv__delete__() {
+ // see note below in ActorDestroy
+ mIPCClosed = true;
+ return IPC_OK();
+}
+
+void StunAddrsRequestParent::OnQueryComplete(const nsACString& hostname,
+ const Maybe<nsCString>& address) {
+ RUN_ON_THREAD(mMainThread,
+ WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
+ &StunAddrsRequestParent::OnQueryComplete_m,
+ nsCString(hostname), address),
+ NS_DISPATCH_NORMAL);
+}
+
+void StunAddrsRequestParent::ActorDestroy(ActorDestroyReason why) {
+ // We may still have refcount>0 if we haven't made it through
+ // GetStunAddrs_s and SendStunAddrs_m yet, but child process
+ // has crashed. We must not send any more msgs to child, or
+ // IPDL will kill chrome process, too.
+ mIPCClosed = true;
+
+ // We need to stop the mDNS service here to ensure that we don't
+ // end up with any messages queued for the main thread after the
+ // destructors run. Because of Bug 1569311, all of the
+ // StunAddrsRequestParent instances end up being destroyed one
+ // after the other, so it is ok to free the shared service when
+ // the first one is destroyed rather than waiting for the last one.
+ // If this behaviour changes, we would potentially end up starting
+ // and stopping instances repeatedly and should add a refcount and
+ // a way of cancelling pending queries to avoid churn in that case.
+ if (mSharedMDNSService) {
+ mSharedMDNSService = nullptr;
+ }
+}
+
+void StunAddrsRequestParent::GetStunAddrs_s() {
+ ASSERT_ON_THREAD(mSTSThread);
+
+ // get the stun addresses while on STS thread
+ NrIceStunAddrArray addrs = NrIceCtx::GetStunAddrs();
+
+ if (mIPCClosed) {
+ return;
+ }
+
+ // in order to return the result over IPC, we need to be on main thread
+ RUN_ON_THREAD(
+ mMainThread,
+ WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
+ &StunAddrsRequestParent::SendStunAddrs_m, std::move(addrs)),
+ NS_DISPATCH_NORMAL);
+}
+
+void StunAddrsRequestParent::SendStunAddrs_m(const NrIceStunAddrArray& addrs) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ // nothing to do: child probably crashed
+ return;
+ }
+
+ // This means that the mDNS service will continue running until shutdown
+ // once started. The StunAddrsRequestParent destructor does not run until
+ // shutdown anyway (see Bug 1569311), so there is not much we can do about
+ // this here. One option would be to add a check if there are no hostnames
+ // registered after UnregisterHostname is called, and if so, stop the mDNS
+ // service at that time (see Bug 1569955.)
+ if (!mSharedMDNSService) {
+ std::ostringstream o;
+ char buffer[16];
+ for (auto& addr : addrs) {
+ if (addr.localAddr().addr.ip_version == NR_IPV4 &&
+ !nr_transport_addr_is_loopback(&addr.localAddr().addr)) {
+ nr_transport_addr_get_addrstring(&addr.localAddr().addr, buffer, 16);
+ o << buffer << ";";
+ }
+ }
+ std::string addrstring = o.str();
+ if (!addrstring.empty()) {
+ mSharedMDNSService = new MDNSServiceWrapper(addrstring);
+ }
+ }
+
+ // send the new addresses back to the child
+ Unused << SendOnStunAddrsAvailable(addrs);
+}
+
+void StunAddrsRequestParent::OnQueryComplete_m(
+ const nsACString& hostname, const Maybe<nsCString>& address) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ // nothing to do: child probably crashed
+ return;
+ }
+
+ // send the hostname and address back to the child
+ Unused << SendOnMDNSQueryComplete(hostname, address);
+}
+
+StaticRefPtr<StunAddrsRequestParent::MDNSServiceWrapper>
+ StunAddrsRequestParent::mSharedMDNSService;
+
+NS_IMPL_ADDREF(StunAddrsRequestParent)
+NS_IMPL_RELEASE(StunAddrsRequestParent)
+
+StunAddrsRequestParent::MDNSServiceWrapper::MDNSServiceWrapper(
+ const std::string& ifaddr)
+ : ifaddr(ifaddr) {}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::RegisterHostname(
+ const char* hostname, const char* address) {
+ StartIfRequired();
+ if (mMDNSService) {
+ mdns_service_register_hostname(mMDNSService, hostname, address);
+ }
+}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::QueryHostname(
+ void* data, const char* hostname) {
+ StartIfRequired();
+ if (mMDNSService) {
+ mdns_service_query_hostname(mMDNSService, data, mdns_service_resolved,
+ mdns_service_timedout, hostname);
+ }
+}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::UnregisterHostname(
+ const char* hostname) {
+ StartIfRequired();
+ if (mMDNSService) {
+ mdns_service_unregister_hostname(mMDNSService, hostname);
+ }
+}
+
+StunAddrsRequestParent::MDNSServiceWrapper::~MDNSServiceWrapper() {
+ if (mMDNSService) {
+ mdns_service_stop(mMDNSService);
+ mMDNSService = nullptr;
+ }
+}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::StartIfRequired() {
+ if (!mMDNSService) {
+ mMDNSService = mdns_service_start(ifaddr.c_str());
+ }
+}
+
+NS_IMPL_ADDREF(StunAddrsRequestParent::MDNSServiceWrapper)
+NS_IMPL_RELEASE(StunAddrsRequestParent::MDNSServiceWrapper)
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h
new file mode 100644
index 0000000000..33e71abbc7
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h
@@ -0,0 +1,82 @@
+/* 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 mozilla_net_StunAddrsRequestParent_h
+#define mozilla_net_StunAddrsRequestParent_h
+
+#include "mozilla/net/PStunAddrsRequestParent.h"
+
+struct MDNSService;
+
+namespace mozilla::net {
+
+class StunAddrsRequestParent : public PStunAddrsRequestParent {
+ friend class PStunAddrsRequestParent;
+
+ public:
+ StunAddrsRequestParent();
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ mozilla::ipc::IPCResult Recv__delete__() override;
+
+ void OnQueryComplete(const nsACString& hostname,
+ const Maybe<nsCString>& address);
+
+ protected:
+ virtual ~StunAddrsRequestParent();
+
+ virtual mozilla::ipc::IPCResult RecvGetStunAddrs() override;
+ virtual mozilla::ipc::IPCResult RecvRegisterMDNSHostname(
+ const nsACString& hostname, const nsACString& address) override;
+ virtual mozilla::ipc::IPCResult RecvQueryMDNSHostname(
+ const nsACString& hostname) override;
+ virtual mozilla::ipc::IPCResult RecvUnregisterMDNSHostname(
+ const nsACString& hostname) override;
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ nsCOMPtr<nsIThread> mMainThread;
+ nsCOMPtr<nsISerialEventTarget> mSTSThread;
+
+ void GetStunAddrs_s();
+ void SendStunAddrs_m(const NrIceStunAddrArray& addrs);
+
+ void OnQueryComplete_m(const nsACString& hostname,
+ const Maybe<nsCString>& address);
+
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+
+ private:
+ bool mIPCClosed; // true if IPDL channel has been closed (child crash)
+
+ class MDNSServiceWrapper {
+ public:
+ explicit MDNSServiceWrapper(const std::string& ifaddr);
+ void RegisterHostname(const char* hostname, const char* address);
+ void QueryHostname(void* data, const char* hostname);
+ void UnregisterHostname(const char* hostname);
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ protected:
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+
+ private:
+ virtual ~MDNSServiceWrapper();
+ void StartIfRequired();
+
+ std::string ifaddr;
+ MDNSService* mMDNSService = nullptr;
+ };
+
+ static StaticRefPtr<MDNSServiceWrapper> mSharedMDNSService;
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_StunAddrsRequestParent_h
diff --git a/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh b/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh
new file mode 100644
index 0000000000..e93e82a6a3
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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/. */
+
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+include NeckoChannelParams;
+
+namespace mozilla {
+namespace net {
+
+struct WebrtcProxyConfig {
+ TabId tabId;
+ nsCString alpn;
+ LoadInfoArgs loadInfoArgs;
+ bool forceProxy;
+};
+
+} // namespace net
+} // namespace mozilla
+
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp
new file mode 100644
index 0000000000..b0a7d37a0e
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp
@@ -0,0 +1,785 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocket.h"
+
+#include "nsHttpChannel.h"
+#include "nsIChannel.h"
+#include "nsIClassOfService.h"
+#include "nsIContentPolicy.h"
+#include "nsIIOService.h"
+#include "nsILoadInfo.h"
+#include "nsIProtocolProxyService.h"
+#include "nsIURIMutator.h"
+#include "nsICookieJarSettings.h"
+#include "nsProxyRelease.h"
+#include "nsString.h"
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "nsISocketTransportService.h"
+#include "nsICancelable.h"
+#include "nsSocketTransportService2.h"
+
+#include "WebrtcTCPSocketCallback.h"
+#include "WebrtcTCPSocketLog.h"
+
+namespace mozilla::net {
+
+class WebrtcTCPData {
+ public:
+ explicit WebrtcTCPData(nsTArray<uint8_t>&& aData) : mData(std::move(aData)) {
+ MOZ_COUNT_CTOR(WebrtcTCPData);
+ }
+
+ MOZ_COUNTED_DTOR(WebrtcTCPData)
+
+ const nsTArray<uint8_t>& GetData() const { return mData; }
+
+ private:
+ nsTArray<uint8_t> mData;
+};
+
+NS_IMPL_ISUPPORTS(WebrtcTCPSocket, nsIAuthPromptProvider,
+ nsIHttpUpgradeListener, nsIInputStreamCallback,
+ nsIInterfaceRequestor, nsIOutputStreamCallback,
+ nsIRequestObserver, nsIStreamListener,
+ nsIProtocolProxyCallback)
+
+WebrtcTCPSocket::WebrtcTCPSocket(WebrtcTCPSocketCallback* aCallbacks)
+ : mProxyCallbacks(aCallbacks),
+ mClosed(false),
+ mOpened(false),
+ mWriteOffset(0),
+ mAuthProvider(nullptr),
+ mTransport(nullptr),
+ mSocketIn(nullptr),
+ mSocketOut(nullptr) {
+ LOG(("WebrtcTCPSocket::WebrtcTCPSocket %p\n", this));
+ mMainThread = GetMainThreadEventTarget();
+ mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ MOZ_RELEASE_ASSERT(mMainThread, "no main thread");
+ MOZ_RELEASE_ASSERT(mSocketThread, "no socket thread");
+}
+
+WebrtcTCPSocket::~WebrtcTCPSocket() {
+ LOG(("WebrtcTCPSocket::~WebrtcTCPSocket %p\n", this));
+
+ NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread,
+ mAuthProvider.forget());
+}
+
+void WebrtcTCPSocket::SetTabId(dom::TabId aTabId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton();
+ if (cpm) {
+ dom::ContentParentId cpId = cpm->GetTabProcessId(aTabId);
+ mAuthProvider = cpm->GetBrowserParentByProcessAndTabId(cpId, aTabId);
+ }
+}
+
+nsresult WebrtcTCPSocket::Write(nsTArray<uint8_t>&& aWriteData) {
+ LOG(("WebrtcTCPSocket::Write %p\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+ nsresult rv = mSocketThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>(
+ "WebrtcTCPSocket::Write", this, &WebrtcTCPSocket::EnqueueWrite_s,
+ std::move(aWriteData)));
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS");
+
+ return rv;
+}
+
+nsresult WebrtcTCPSocket::Close() {
+ LOG(("WebrtcTCPSocket::Close %p\n", this));
+
+ CloseWithReason(NS_OK);
+
+ return NS_OK;
+}
+
+void WebrtcTCPSocket::CloseWithReason(nsresult aReason) {
+ LOG(("WebrtcTCPSocket::CloseWithReason %p reason=%u\n", this,
+ static_cast<uint32_t>(aReason)));
+
+ if (!OnSocketThread()) {
+ MOZ_ASSERT(NS_IsMainThread(), "not on main thread");
+
+ // Let's pretend we got an open even if we didn't to prevent an Open later.
+ mOpened = true;
+
+ DebugOnly<nsresult> rv =
+ mSocketThread->Dispatch(NewRunnableMethod<nsresult>(
+ "WebrtcTCPSocket::CloseWithReason", this,
+ &WebrtcTCPSocket::CloseWithReason, aReason));
+
+ // This was MOZ_ALWAYS_SUCCEEDS, but that now uses NS_WARNING_ASSERTION.
+ // In order to convert this back to MOZ_ALWAYS_SUCCEEDS we would need
+ // OnSocketThread to return true if we're shutting down and doing the
+ // "running all of STS's queued events on main" thing.
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS");
+
+ return;
+ }
+
+ if (mClosed) {
+ return;
+ }
+
+ mClosed = true;
+
+ if (mSocketIn) {
+ mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
+ mSocketIn = nullptr;
+ }
+
+ if (mSocketOut) {
+ mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
+ mSocketOut = nullptr;
+ }
+
+ if (mTransport) {
+ mTransport->Close(NS_BASE_STREAM_CLOSED);
+ mTransport = nullptr;
+ }
+
+ NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread,
+ mAuthProvider.forget());
+ InvokeOnClose(aReason);
+}
+
+nsresult WebrtcTCPSocket::Open(
+ const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress,
+ const int& aLocalPort, bool aUseTls,
+ const Maybe<net::WebrtcProxyConfig>& aProxyConfig) {
+ LOG(("WebrtcTCPSocket::Open %p remote-host=%s local-addr=%s local-port=%d",
+ this, PromiseFlatCString(aHost).get(),
+ PromiseFlatCString(aLocalAddress).get(), aLocalPort));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (NS_WARN_IF(mOpened)) {
+ LOG(("WebrtcTCPSocket %p: TCP socket already open\n", this));
+ CloseWithReason(NS_ERROR_FAILURE);
+ return NS_ERROR_FAILURE;
+ }
+
+ mOpened = true;
+ const nsLiteralCString schemePrefix = aUseTls ? "https://"_ns : "http://"_ns;
+ nsAutoCString spec(schemePrefix);
+
+ bool ipv6Literal = aHost.Find(":") != kNotFound;
+ if (ipv6Literal) {
+ spec += "[";
+ spec += aHost;
+ spec += "]";
+ } else {
+ spec += aHost;
+ }
+
+ nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .SetSpec(spec)
+ .SetPort(aPort)
+ .Finalize(mURI);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return NS_ERROR_FAILURE;
+ }
+
+ mTls = aUseTls;
+ mLocalAddress = aLocalAddress;
+ mLocalPort = aLocalPort;
+ mProxyConfig = aProxyConfig;
+
+ if (!mProxyConfig.isSome()) {
+ OpenWithoutHttpProxy(nullptr);
+ return NS_OK;
+ }
+
+ // We need to figure out whether a proxy needs to be used for mURI before
+ // we can start on establishing a connection.
+ rv = DoProxyConfigLookup();
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ }
+
+ return rv;
+}
+
+nsresult WebrtcTCPSocket::DoProxyConfigLookup() {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsresult rv;
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel), mURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = pps->AsyncResolve(channel,
+ nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
+ nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
+ this, nullptr, getter_AddRefs(mProxyRequest));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // We pick back up in OnProxyAvailable
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebrtcTCPSocket::OnProxyAvailable(nsICancelable* aRequest,
+ nsIChannel* aChannel,
+ nsIProxyInfo* aProxyinfo,
+ nsresult aResult) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxyRequest = nullptr;
+
+ if (NS_SUCCEEDED(aResult) && aProxyinfo) {
+ nsresult rv = aProxyinfo->GetType(mProxyType);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return rv;
+ }
+
+ if (mProxyType == "http" || mProxyType == "https") {
+ rv = OpenWithHttpProxy();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ }
+ return rv;
+ }
+
+ if (mProxyType == "socks" || mProxyType == "socks4" ||
+ mProxyType == "direct") {
+ OpenWithoutHttpProxy(aProxyinfo);
+ return NS_OK;
+ }
+ }
+
+ OpenWithoutHttpProxy(nullptr);
+
+ return NS_OK;
+}
+
+void WebrtcTCPSocket::OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo) {
+ if (!OnSocketThread()) {
+ DebugOnly<nsresult> rv =
+ mSocketThread->Dispatch(NewRunnableMethod<nsCOMPtr<nsIProxyInfo>>(
+ "WebrtcTCPSocket::OpenWithoutHttpProxy", this,
+ &WebrtcTCPSocket::OpenWithoutHttpProxy, aSocksProxyInfo));
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS");
+ return;
+ }
+
+ LOG(("WebrtcTCPSocket::OpenWithoutHttpProxy %p\n", this));
+
+ if (mClosed) {
+ return;
+ }
+
+ if (NS_WARN_IF(mProxyConfig.isSome() && mProxyConfig->forceProxy() &&
+ !aSocksProxyInfo)) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return;
+ }
+
+ nsCString host;
+ int32_t port;
+
+ nsresult rv = mURI->GetHost(host);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ rv = mURI->GetPort(&port);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ AutoTArray<nsCString, 1> socketTypes;
+ if (mTls) {
+ socketTypes.AppendElement("ssl"_ns);
+ }
+
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService("@mozilla.org/network/socket-transport-service;1");
+ rv = sts->CreateTransport(socketTypes, host, port, aSocksProxyInfo, nullptr,
+ getter_AddRefs(mTransport));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ mTransport->SetReuseAddrPort(true);
+
+ PRNetAddr prAddr;
+ if (NS_WARN_IF(PR_SUCCESS !=
+ PR_InitializeNetAddr(PR_IpAddrAny, mLocalPort, &prAddr))) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return;
+ }
+
+ if (NS_WARN_IF(PR_SUCCESS !=
+ PR_StringToNetAddr(mLocalAddress.BeginReading(), &prAddr))) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return;
+ }
+
+ mozilla::net::NetAddr addr(&prAddr);
+ rv = mTransport->Bind(&addr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ // Binding to a V4 address is not sufficient to cause this socket to use
+ // V4, and the same goes for V6. So, we disable as needed here.
+ uint32_t flags = 0;
+ if (addr.raw.family == AF_INET) {
+ flags |= nsISocketTransport::DISABLE_IPV6;
+ } else if (addr.raw.family == AF_INET6) {
+ flags |= nsISocketTransport::DISABLE_IPV4;
+ } else {
+ MOZ_CRASH();
+ }
+
+ mTransport->SetConnectionFlags(flags);
+
+ nsCOMPtr<nsIInputStream> socketIn;
+ rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(socketIn));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+ mSocketIn = do_QueryInterface(socketIn);
+ if (NS_WARN_IF(!mSocketIn)) {
+ CloseWithReason(NS_ERROR_NULL_POINTER);
+ return;
+ }
+
+ nsCOMPtr<nsIOutputStream> socketOut;
+ rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
+ getter_AddRefs(socketOut));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+ mSocketOut = do_QueryInterface(socketOut);
+ if (NS_WARN_IF(!mSocketOut)) {
+ CloseWithReason(NS_ERROR_NULL_POINTER);
+ return;
+ }
+
+ FinishOpen();
+}
+
+nsresult WebrtcTCPSocket::OpenWithHttpProxy() {
+ MOZ_ASSERT(NS_IsMainThread(), "not on main thread");
+ LOG(("WebrtcTCPSocket::OpenWithHttpProxy %p\n", this));
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ioService;
+ ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: io service missing\n", this));
+ return rv;
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ Maybe<net::LoadInfoArgs> loadInfoArgs = Some(mProxyConfig->loadInfoArgs());
+
+ // FIXME: We don't know the remote type of the process which provided these
+ // LoadInfoArgs. Pass in `NOT_REMOTE_TYPE` as the origin process to blindly
+ // accept whatever value was passed by the other side for now, as we aren't
+ // using it for security checks here.
+ // If this code ever starts checking the triggering remote type, this needs to
+ // be changed.
+ rv = ipc::LoadInfoArgsToLoadInfo(loadInfoArgs, NOT_REMOTE_TYPE,
+ getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: could not init load info\n", this));
+ return rv;
+ }
+
+ // -need to always tunnel since we're using a proxy
+ // -there shouldn't be an opportunity to send cookies, but explicitly disallow
+ // them anyway.
+ // -the previous proxy tunnel didn't support redirects e.g. 307. don't need to
+ // introduce new behavior. can't follow redirects on connect anyway.
+ nsCOMPtr<nsIChannel> localChannel;
+ rv = ioService->NewChannelFromURIWithProxyFlags(
+ mURI, nullptr,
+ // Proxy flags are overridden by SetConnectOnly()
+ 0, loadInfo->LoadingNode(), loadInfo->GetLoadingPrincipal(),
+ loadInfo->TriggeringPrincipal(),
+ nsILoadInfo::SEC_COOKIES_OMIT |
+ // We need this flag to allow loads from any origin since this channel
+ // is being used to CONNECT to an HTTP proxy.
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA,
+ getter_AddRefs(localChannel));
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: bad open channel\n", this));
+ return rv;
+ }
+
+ nsCOMPtr<nsILoadInfo> channelLoadInfo = localChannel->LoadInfo();
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+ loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
+ channelLoadInfo->SetCookieJarSettings(cookieJarSettings);
+
+ RefPtr<nsHttpChannel> httpChannel;
+ CallQueryInterface(localChannel, httpChannel.StartAssignment());
+
+ if (!httpChannel) {
+ LOG(("WebrtcTCPSocket %p: not an http channel\n", this));
+ return NS_ERROR_FAILURE;
+ }
+
+ httpChannel->SetNotificationCallbacks(this);
+
+ // don't block webrtc proxy setup with other requests
+ // often more than one of these channels will be created all at once by ICE
+ nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(localChannel);
+ if (cos) {
+ cos->AddClassFlags(nsIClassOfService::Unblocked |
+ nsIClassOfService::DontThrottle);
+ } else {
+ LOG(("WebrtcTCPSocket %p: could not set class of service\n", this));
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = httpChannel->HTTPUpgrade(mProxyConfig->alpn(), this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ rv = httpChannel->SetConnectOnly();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = httpChannel->AsyncOpen(this);
+
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: cannot async open\n", this));
+ return rv;
+ }
+
+ // This picks back up in OnTransportAvailable once we have connected to the
+ // proxy, and performed the http upgrade to switch the proxy into passthrough
+ // mode.
+
+ return NS_OK;
+}
+
+void WebrtcTCPSocket::EnqueueWrite_s(nsTArray<uint8_t>&& aWriteData) {
+ LOG(("WebrtcTCPSocket::EnqueueWrite %p\n", this));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ if (mClosed) {
+ return;
+ }
+
+ mWriteQueue.emplace_back(std::move(aWriteData));
+
+ if (mSocketOut) {
+ mSocketOut->AsyncWait(this, 0, 0, nullptr);
+ }
+}
+
+void WebrtcTCPSocket::InvokeOnClose(nsresult aReason) {
+ LOG(("WebrtcTCPSocket::InvokeOnClose %p\n", this));
+
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch(
+ NewRunnableMethod<nsresult>("WebrtcTCPSocket::InvokeOnClose", this,
+ &WebrtcTCPSocket::InvokeOnClose, aReason)));
+ return;
+ }
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null");
+
+ if (mProxyRequest) {
+ mProxyRequest->Cancel(aReason);
+ mProxyRequest = nullptr;
+ }
+
+ mProxyCallbacks->OnClose(aReason);
+ mProxyCallbacks = nullptr;
+}
+
+void WebrtcTCPSocket::InvokeOnConnected() {
+ LOG(("WebrtcTCPSocket::InvokeOnConnected %p\n", this));
+
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch(
+ NewRunnableMethod("WebrtcTCPSocket::InvokeOnConnected", this,
+ &WebrtcTCPSocket::InvokeOnConnected)));
+ return;
+ }
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null");
+
+ mProxyCallbacks->OnConnected(mProxyType);
+}
+
+void WebrtcTCPSocket::InvokeOnRead(nsTArray<uint8_t>&& aReadData) {
+ LOG(("WebrtcTCPSocket::InvokeOnRead %p count=%zu\n", this,
+ aReadData.Length()));
+
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(
+ mMainThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>(
+ "WebrtcTCPSocket::InvokeOnRead", this,
+ &WebrtcTCPSocket::InvokeOnRead, std::move(aReadData))));
+ return;
+ }
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null");
+
+ mProxyCallbacks->OnRead(std::move(aReadData));
+}
+
+// nsIHttpUpgradeListener
+NS_IMETHODIMP
+WebrtcTCPSocket::OnTransportAvailable(nsISocketTransport* aTransport,
+ nsIAsyncInputStream* aSocketIn,
+ nsIAsyncOutputStream* aSocketOut) {
+ // This is called only in the http proxy case, once we have connected to the
+ // http proxy and performed the http upgrade to switch it over to passthrough
+ // mode. That process is started async by OpenWithHttpProxy.
+ LOG(("WebrtcTCPSocket::OnTransportAvailable %p\n", this));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mTransport,
+ "already called transport available on webrtc TCP socket");
+
+ // Cancel any pending callbacks. The caller doesn't always cancel these
+ // awaits. We need to make sure they don't get them.
+ aSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
+ aSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
+
+ if (mClosed) {
+ LOG(("WebrtcTCPSocket::OnTransportAvailable %p closed\n", this));
+ return NS_OK;
+ }
+
+ mTransport = aTransport;
+ mSocketIn = aSocketIn;
+ mSocketOut = aSocketOut;
+
+ // pulled from nr_socket_prsock.cpp
+ uint32_t minBufferSize = 256 * 1024;
+ nsresult rv = mTransport->SetSendBufferSize(minBufferSize);
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcProxyChannel::OnTransportAvailable %p send failed\n", this));
+ CloseWithReason(rv);
+ return rv;
+ }
+ rv = mTransport->SetRecvBufferSize(minBufferSize);
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcProxyChannel::OnTransportAvailable %p recv failed\n", this));
+ CloseWithReason(rv);
+ return rv;
+ }
+
+ FinishOpen();
+ return NS_OK;
+}
+
+void WebrtcTCPSocket::FinishOpen() {
+ MOZ_ASSERT(OnSocketThread());
+ // mTransport, mSocketIn, and mSocketOut are all set. We may have set them in
+ // OnTransportAvailable (in the http/https proxy case), or in
+ // OpenWithoutHttpProxy. From here on out, this class functions the same for
+ // these two cases.
+
+ mSocketIn->AsyncWait(this, 0, 0, nullptr);
+
+ InvokeOnConnected();
+}
+
+NS_IMETHODIMP
+WebrtcTCPSocket::OnUpgradeFailed(nsresult aErrorCode) {
+ LOG(("WebrtcTCPSocket::OnUpgradeFailed %p\n", this));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mTransport,
+ "already called transport available on webrtc TCP socket");
+
+ if (mClosed) {
+ LOG(("WebrtcTCPSocket::OnUpgradeFailed %p closed\n", this));
+ return NS_OK;
+ }
+
+ CloseWithReason(aErrorCode);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebrtcTCPSocket::OnWebSocketConnectionAvailable(
+ WebSocketConnectionBase* aConnection) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIRequestObserver (from nsIStreamListener)
+NS_IMETHODIMP
+WebrtcTCPSocket::OnStartRequest(nsIRequest* aRequest) {
+ LOG(("WebrtcTCPSocket::OnStartRequest %p\n", this));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebrtcTCPSocket::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
+ LOG(("WebrtcTCPSocket::OnStopRequest %p status=%u\n", this,
+ static_cast<uint32_t>(aStatusCode)));
+
+ // see nsHttpChannel::ProcessFailedProxyConnect for most error codes
+ if (NS_FAILED(aStatusCode)) {
+ CloseWithReason(aStatusCode);
+ return aStatusCode;
+ }
+
+ return NS_OK;
+}
+
+// nsIStreamListener
+NS_IMETHODIMP
+WebrtcTCPSocket::OnDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aInputStream, uint64_t aOffset,
+ uint32_t aCount) {
+ LOG(("WebrtcTCPSocket::OnDataAvailable %p count=%u\n", this, aCount));
+ MOZ_ASSERT(0, "unreachable data available");
+ return NS_OK;
+}
+
+// nsIInputStreamCallback
+NS_IMETHODIMP
+WebrtcTCPSocket::OnInputStreamReady(nsIAsyncInputStream* in) {
+ LOG(("WebrtcTCPSocket::OnInputStreamReady %p unwritten=%zu\n", this,
+ CountUnwrittenBytes()));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mClosed, "webrtc TCP socket closed");
+ MOZ_ASSERT(mTransport, "webrtc TCP socket not connected");
+ MOZ_ASSERT(mSocketIn == in, "wrong input stream");
+
+ char buffer[9216];
+ uint32_t remainingCapacity = sizeof(buffer);
+ uint32_t read = 0;
+
+ while (remainingCapacity > 0) {
+ uint32_t count = 0;
+ nsresult rv = mSocketIn->Read(buffer + read, remainingCapacity, &count);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ break;
+ }
+
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket::OnInputStreamReady %p failed %u\n", this,
+ static_cast<uint32_t>(rv)));
+ CloseWithReason(rv);
+ return rv;
+ }
+
+ // base stream closed
+ if (count == 0) {
+ LOG(("WebrtcTCPSocket::OnInputStreamReady %p connection closed\n", this));
+ CloseWithReason(NS_ERROR_FAILURE);
+ return NS_OK;
+ }
+
+ remainingCapacity -= count;
+ read += count;
+ }
+
+ if (read > 0) {
+ nsTArray<uint8_t> array(read);
+ array.AppendElements(buffer, read);
+
+ InvokeOnRead(std::move(array));
+ }
+
+ mSocketIn->AsyncWait(this, 0, 0, nullptr);
+
+ return NS_OK;
+}
+
+// nsIOutputStreamCallback
+NS_IMETHODIMP
+WebrtcTCPSocket::OnOutputStreamReady(nsIAsyncOutputStream* out) {
+ LOG(("WebrtcTCPSocket::OnOutputStreamReady %p unwritten=%zu\n", this,
+ CountUnwrittenBytes()));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mClosed, "webrtc TCP socket closed");
+ MOZ_ASSERT(mTransport, "webrtc TCP socket not connected");
+ MOZ_ASSERT(mSocketOut == out, "wrong output stream");
+
+ while (!mWriteQueue.empty()) {
+ const WebrtcTCPData& data = mWriteQueue.front();
+
+ char* buffer = reinterpret_cast<char*>(
+ const_cast<uint8_t*>(data.GetData().Elements())) +
+ mWriteOffset;
+ uint32_t toWrite = data.GetData().Length() - mWriteOffset;
+
+ uint32_t wrote = 0;
+ nsresult rv = mSocketOut->Write(buffer, toWrite, &wrote);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ mSocketOut->AsyncWait(this, 0, 0, nullptr);
+ return NS_OK;
+ }
+
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket::OnOutputStreamReady %p failed %u\n", this,
+ static_cast<uint32_t>(rv)));
+ CloseWithReason(rv);
+ return NS_OK;
+ }
+
+ mWriteOffset += wrote;
+
+ if (toWrite == wrote) {
+ mWriteOffset = 0;
+ mWriteQueue.pop_front();
+ }
+ }
+
+ return NS_OK;
+}
+
+// nsIInterfaceRequestor
+NS_IMETHODIMP
+WebrtcTCPSocket::GetInterface(const nsIID& iid, void** result) {
+ LOG(("WebrtcTCPSocket::GetInterface %p\n", this));
+
+ return QueryInterface(iid, result);
+}
+
+size_t WebrtcTCPSocket::CountUnwrittenBytes() const {
+ size_t count = 0;
+
+ for (const WebrtcTCPData& data : mWriteQueue) {
+ count += data.GetData().Length();
+ }
+
+ MOZ_ASSERT(count >= mWriteOffset, "offset exceeds write buffer length");
+
+ count -= mWriteOffset;
+
+ return count;
+}
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h
new file mode 100644
index 0000000000..632ba47d32
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 webrtc_tcp_socket_h__
+#define webrtc_tcp_socket_h__
+
+#include <list>
+
+#include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIAuthPromptProvider.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIStreamListener.h"
+#include "nsStringFwd.h"
+#include "nsTArray.h"
+#include "nsIProtocolProxyCallback.h"
+#include "mozilla/net/WebrtcProxyConfig.h"
+
+class nsISocketTransport;
+
+namespace mozilla::net {
+
+class WebrtcTCPSocketCallback;
+class WebrtcTCPData;
+
+class WebrtcTCPSocket : public nsIHttpUpgradeListener,
+ public nsIStreamListener,
+ public nsIInputStreamCallback,
+ public nsIOutputStreamCallback,
+ public nsIInterfaceRequestor,
+ public nsIAuthPromptProvider,
+ public nsIProtocolProxyCallback {
+ public:
+ NS_DECL_NSIHTTPUPGRADELISTENER
+ NS_DECL_NSIINPUTSTREAMCALLBACK
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIOUTPUTSTREAMCALLBACK
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_SAFE_NSIAUTHPROMPTPROVIDER(mAuthProvider)
+ NS_DECL_NSIPROTOCOLPROXYCALLBACK
+
+ explicit WebrtcTCPSocket(WebrtcTCPSocketCallback* aCallbacks);
+
+ void SetTabId(dom::TabId aTabId);
+ nsresult Open(const nsACString& aHost, const int& aPort,
+ const nsACString& aLocalAddress, const int& aLocalPort,
+ bool aUseTls,
+ const Maybe<net::WebrtcProxyConfig>& aProxyConfig);
+ nsresult Write(nsTArray<uint8_t>&& aBytes);
+ nsresult Close();
+
+ size_t CountUnwrittenBytes() const;
+
+ protected:
+ virtual ~WebrtcTCPSocket();
+
+ // protected for gtests
+ virtual void InvokeOnClose(nsresult aReason);
+ virtual void InvokeOnConnected();
+ virtual void InvokeOnRead(nsTArray<uint8_t>&& aReadData);
+
+ RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks;
+
+ private:
+ bool mClosed;
+ bool mOpened;
+ nsCOMPtr<nsIURI> mURI;
+ bool mTls = false;
+ Maybe<WebrtcProxyConfig> mProxyConfig;
+ nsCString mLocalAddress;
+ uint16_t mLocalPort = 0;
+ nsCString mProxyType;
+
+ nsresult DoProxyConfigLookup();
+ nsresult OpenWithHttpProxy();
+ void OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo);
+ void FinishOpen();
+ void EnqueueWrite_s(nsTArray<uint8_t>&& aWriteData);
+
+ void CloseWithReason(nsresult aReason);
+
+ size_t mWriteOffset;
+ std::list<WebrtcTCPData> mWriteQueue;
+ nsCOMPtr<nsIAuthPromptProvider> mAuthProvider;
+
+ // Indicates that the channel is CONNECTed
+ nsCOMPtr<nsISocketTransport> mTransport;
+ nsCOMPtr<nsIAsyncInputStream> mSocketIn;
+ nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
+ nsCOMPtr<nsIEventTarget> mMainThread;
+ nsCOMPtr<nsIEventTarget> mSocketThread;
+ nsCOMPtr<nsICancelable> mProxyRequest;
+};
+
+} // namespace mozilla::net
+
+#endif // webrtc_tcp_socket_h__
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h
new file mode 100644
index 0000000000..1929e55ac2
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 webrtc_tcp_socket_callback_h__
+#define webrtc_tcp_socket_callback_h__
+
+#include "nsTArray.h"
+
+namespace mozilla::net {
+
+class WebrtcTCPSocketCallback {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ virtual void OnClose(nsresult aReason) = 0;
+ virtual void OnConnected(const nsACString& aProxyType) = 0;
+ virtual void OnRead(nsTArray<uint8_t>&& aReadData) = 0;
+
+ protected:
+ virtual ~WebrtcTCPSocketCallback() = default;
+};
+
+} // namespace mozilla::net
+
+#endif // webrtc_tcp_socket_callback_h__
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp
new file mode 100644
index 0000000000..52d1ba8ab2
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocketChild.h"
+
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/SocketProcessChild.h"
+
+#include "LoadInfo.h"
+
+#include "WebrtcTCPSocketLog.h"
+#include "WebrtcTCPSocketCallback.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnClose(
+ const nsresult& aReason) {
+ LOG(("WebrtcTCPSocketChild::RecvOnClose %p\n", this));
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+ mProxyCallbacks->OnClose(aReason);
+ mProxyCallbacks = nullptr;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnConnected(
+ const nsACString& aProxyType) {
+ LOG(("WebrtcTCPSocketChild::RecvOnConnected %p\n", this));
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+ mProxyCallbacks->OnConnected(aProxyType);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnRead(
+ nsTArray<uint8_t>&& aReadData) {
+ LOG(("WebrtcTCPSocketChild::RecvOnRead %p\n", this));
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+ mProxyCallbacks->OnRead(std::move(aReadData));
+
+ return IPC_OK();
+}
+
+WebrtcTCPSocketChild::WebrtcTCPSocketChild(
+ WebrtcTCPSocketCallback* aProxyCallbacks)
+ : mProxyCallbacks(aProxyCallbacks) {
+ MOZ_COUNT_CTOR(WebrtcTCPSocketChild);
+
+ LOG(("WebrtcTCPSocketChild::WebrtcTCPSocketChild %p\n", this));
+}
+
+WebrtcTCPSocketChild::~WebrtcTCPSocketChild() {
+ MOZ_COUNT_DTOR(WebrtcTCPSocketChild);
+
+ LOG(("WebrtcTCPSocketChild::~WebrtcTCPSocketChild %p\n", this));
+}
+
+void WebrtcTCPSocketChild::AsyncOpen(
+ const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress,
+ const int& aLocalPort, bool aUseTls,
+ const std::shared_ptr<NrSocketProxyConfig>& aProxyConfig) {
+ LOG(("WebrtcTCPSocketChild::AsyncOpen %p %s:%d\n", this,
+ PromiseFlatCString(aHost).get(), aPort));
+
+ MOZ_ASSERT(NS_IsMainThread(), "not main thread");
+
+ AddIPDLReference();
+
+ Maybe<net::WebrtcProxyConfig> proxyConfig;
+ Maybe<dom::TabId> tabId;
+ if (aProxyConfig) {
+ proxyConfig = Some(aProxyConfig->GetConfig());
+ tabId = Some(proxyConfig->tabId());
+ }
+
+ if (IsNeckoChild()) {
+ // We're on a content process
+ gNeckoChild->SendPWebrtcTCPSocketConstructor(this, tabId);
+ } else if (IsSocketProcessChild()) {
+ // We're on a socket process
+ SocketProcessChild::GetSingleton()->SendPWebrtcTCPSocketConstructor(this,
+ tabId);
+ }
+
+ SendAsyncOpen(aHost, aPort, aLocalAddress, aLocalPort, aUseTls, proxyConfig);
+}
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h
new file mode 100644
index 0000000000..638ffcaac3
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 mozilla_net_WebrtcTCPSocketChild_h
+#define mozilla_net_WebrtcTCPSocketChild_h
+
+#include "mozilla/net/PWebrtcTCPSocketChild.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "transport/nr_socket_proxy_config.h"
+
+namespace mozilla::net {
+
+class WebrtcTCPSocketCallback;
+
+class WebrtcTCPSocketChild : public PWebrtcTCPSocketChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketChild)
+
+ mozilla::ipc::IPCResult RecvOnClose(const nsresult& aReason) override;
+
+ mozilla::ipc::IPCResult RecvOnConnected(
+ const nsACString& aProxyType) override;
+
+ mozilla::ipc::IPCResult RecvOnRead(nsTArray<uint8_t>&& aReadData) override;
+
+ explicit WebrtcTCPSocketChild(WebrtcTCPSocketCallback* aProxyCallbacks);
+
+ void AsyncOpen(const nsACString& aHost, const int& aPort,
+ const nsACString& aLocalAddress, const int& aLocalPort,
+ bool aUseTls,
+ const std::shared_ptr<NrSocketProxyConfig>& aProxyConfig);
+
+ void AddIPDLReference() { AddRef(); }
+ void ReleaseIPDLReference() { Release(); }
+
+ protected:
+ virtual ~WebrtcTCPSocketChild();
+
+ RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks;
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_WebrtcTCPSocketChild_h
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp
new file mode 100644
index 0000000000..a6b0dcdb75
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp
@@ -0,0 +1,11 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocketLog.h"
+
+namespace mozilla::net {
+LazyLogModule webrtcTCPSocketLog("WebrtcTCPSocket");
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h
new file mode 100644
index 0000000000..72d3d0064b
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 webrtc_tcp_socket_log_h__
+#define webrtc_tcp_socket_log_h__
+
+#include "mozilla/Logging.h"
+
+namespace mozilla::net {
+extern LazyLogModule webrtcTCPSocketLog;
+} // namespace mozilla::net
+
+#undef LOG
+#define LOG(args) \
+ MOZ_LOG(mozilla::net::webrtcTCPSocketLog, mozilla::LogLevel::Debug, args)
+
+#endif // webrtc_tcp_socket_log_h__
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp
new file mode 100644
index 0000000000..0df8962757
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocketParent.h"
+
+#include "mozilla/net/NeckoParent.h"
+
+#include "WebrtcTCPSocket.h"
+#include "WebrtcTCPSocketLog.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvAsyncOpen(
+ const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress,
+ const int& aLocalPort, const bool& aUseTls,
+ const Maybe<WebrtcProxyConfig>& aProxyConfig) {
+ LOG(("WebrtcTCPSocketParent::RecvAsyncOpen %p to %s:%d\n", this,
+ PromiseFlatCString(aHost).get(), aPort));
+
+ MOZ_ASSERT(mChannel, "webrtc TCP socket should be non-null");
+ if (!mChannel) {
+ return IPC_FAIL(this, "Called with null channel.");
+ }
+
+ mChannel->Open(aHost, aPort, aLocalAddress, aLocalPort, aUseTls,
+ aProxyConfig);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvWrite(
+ nsTArray<uint8_t>&& aWriteData) {
+ LOG(("WebrtcTCPSocketParent::RecvWrite %p for %zu\n", this,
+ aWriteData.Length()));
+
+ // Need to check this here in case there are Writes in the queue after OnClose
+ if (mChannel) {
+ mChannel->Write(std::move(aWriteData));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvClose() {
+ LOG(("WebrtcTCPSocketParent::RecvClose %p\n", this));
+
+ CleanupChannel();
+
+ IProtocol* mgr = Manager();
+ if (!Send__delete__(this)) {
+ return IPC_FAIL_NO_REASON(mgr);
+ }
+
+ return IPC_OK();
+}
+
+void WebrtcTCPSocketParent::ActorDestroy(ActorDestroyReason aWhy) {
+ LOG(("WebrtcTCPSocketParent::ActorDestroy %p for %d\n", this, aWhy));
+
+ CleanupChannel();
+}
+
+WebrtcTCPSocketParent::WebrtcTCPSocketParent(const Maybe<dom::TabId>& aTabId) {
+ MOZ_COUNT_CTOR(WebrtcTCPSocketParent);
+
+ LOG(("WebrtcTCPSocketParent::WebrtcTCPSocketParent %p\n", this));
+
+ mChannel = new WebrtcTCPSocket(this);
+ if (aTabId.isSome()) {
+ mChannel->SetTabId(*aTabId);
+ }
+}
+
+WebrtcTCPSocketParent::~WebrtcTCPSocketParent() {
+ MOZ_COUNT_DTOR(WebrtcTCPSocketParent);
+
+ LOG(("WebrtcTCPSocketParent::~WebrtcTCPSocketParent %p\n", this));
+
+ CleanupChannel();
+}
+
+// WebrtcTCPSocketCallback
+void WebrtcTCPSocketParent::OnClose(nsresult aReason) {
+ LOG(("WebrtcTCPSocketParent::OnClose %p\n", this));
+
+ if (mChannel) {
+ Unused << SendOnClose(aReason);
+ }
+
+ CleanupChannel();
+}
+
+void WebrtcTCPSocketParent::OnRead(nsTArray<uint8_t>&& aReadData) {
+ LOG(("WebrtcTCPSocketParent::OnRead %p %zu\n", this, aReadData.Length()));
+
+ if (mChannel && !SendOnRead(std::move(aReadData))) {
+ CleanupChannel();
+ }
+}
+
+void WebrtcTCPSocketParent::OnConnected(const nsACString& aProxyType) {
+ LOG(("WebrtcTCPSocketParent::OnConnected %p\n", this));
+
+ if (mChannel && !SendOnConnected(aProxyType)) {
+ CleanupChannel();
+ }
+}
+
+void WebrtcTCPSocketParent::CleanupChannel() {
+ if (mChannel) {
+ mChannel->Close();
+ mChannel = nullptr;
+ }
+}
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h
new file mode 100644
index 0000000000..df4462609d
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 mozilla_net_WebrtcTCPSocketParent_h
+#define mozilla_net_WebrtcTCPSocketParent_h
+
+#include "mozilla/net/PWebrtcTCPSocketParent.h"
+
+#include "WebrtcTCPSocketCallback.h"
+
+class nsIAuthPromptProvider;
+
+namespace mozilla::net {
+
+class WebrtcTCPSocket;
+
+class WebrtcTCPSocketParent : public PWebrtcTCPSocketParent,
+ public WebrtcTCPSocketCallback {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketParent, override)
+
+ mozilla::ipc::IPCResult RecvAsyncOpen(
+ const nsACString& aHost, const int& aPort,
+ const nsACString& aLocalAddress, const int& aLocalPort,
+ const bool& aUseTls,
+ const Maybe<WebrtcProxyConfig>& aProxyConfig) override;
+
+ mozilla::ipc::IPCResult RecvWrite(nsTArray<uint8_t>&& aWriteData) override;
+
+ mozilla::ipc::IPCResult RecvClose() override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ explicit WebrtcTCPSocketParent(const Maybe<dom::TabId>& aTabId);
+
+ // WebrtcTCPSocketCallback
+ void OnClose(nsresult aReason) override;
+ void OnConnected(const nsACString& aProxyType) override;
+ void OnRead(nsTArray<uint8_t>&& bytes) override;
+
+ void AddIPDLReference() { AddRef(); }
+ void ReleaseIPDLReference() { Release(); }
+
+ protected:
+ virtual ~WebrtcTCPSocketParent();
+
+ private:
+ void CleanupChannel();
+
+ // Indicates that IPC is open.
+ RefPtr<WebrtcTCPSocket> mChannel;
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_WebrtcTCPSocketParent_h
diff --git a/dom/media/webrtc/transport/ipc/moz.build b/dom/media/webrtc/transport/ipc/moz.build
new file mode 100644
index 0000000000..a2a72bb624
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/moz.build
@@ -0,0 +1,54 @@
+# 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/.
+
+EXPORTS.mozilla.net += [
+ "NrIceStunAddrMessageUtils.h",
+ "PStunAddrsParams.h",
+ "StunAddrsRequestChild.h",
+ "StunAddrsRequestParent.h",
+ "WebrtcTCPSocket.h",
+ "WebrtcTCPSocketCallback.h",
+ "WebrtcTCPSocketChild.h",
+ "WebrtcTCPSocketParent.h",
+]
+
+UNIFIED_SOURCES += [
+ "StunAddrsRequestChild.cpp",
+ "StunAddrsRequestParent.cpp",
+ "WebrtcTCPSocket.cpp",
+ "WebrtcTCPSocketChild.cpp",
+ "WebrtcTCPSocketLog.cpp",
+ "WebrtcTCPSocketParent.cpp",
+]
+
+IPDL_SOURCES += [
+ "PStunAddrsRequest.ipdl",
+ "PWebrtcTCPSocket.ipdl",
+ "WebrtcProxyConfig.ipdlh",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+DEFINES["R_DEFINED_INT2"] = "int16_t"
+DEFINES["R_DEFINED_UINT2"] = "uint16_t"
+DEFINES["R_DEFINED_INT4"] = "int32_t"
+DEFINES["R_DEFINED_UINT4"] = "uint32_t"
+# These are defined to avoid a conflict between typedefs in winsock2.h and
+# r_types.h. This is safe because these types are unused by the code here,
+# but still deeply unfortunate. There is similar code in the win32 version of
+# csi_platform.h, but that trick does not work here, even if that file is
+# directly included.
+DEFINES["R_DEFINED_INT8"] = "int8_t"
+DEFINES["R_DEFINED_UINT8"] = "uint8_t"
+
+LOCAL_INCLUDES += [
+ "/dom/media/webrtc/jsapi",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/net",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr",
+ "/media/webrtc",
+ "/netwerk/base",
+ "/netwerk/protocol/http",
+]
diff --git a/dom/media/webrtc/transport/logging.h b/dom/media/webrtc/transport/logging.h
new file mode 100644
index 0000000000..98cbd13819
--- /dev/null
+++ b/dom/media/webrtc/transport/logging.h
@@ -0,0 +1,65 @@
+/* -*- 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"
+
+#ifdef MOZILLA_INTERNAL_API
+
+# 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 mozilla::LogModule* getLogModule() { \
+ static mozilla::LazyLogModule log(n); \
+ return static_cast<mozilla::LogModule*>(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)
+#else
+// When building mtransport outside of XUL, for example in stand-alone gtests,
+// PR_Logging needs to be used instead of mozilla logging.
+
+# include "prlog.h"
+
+# define ML_ERROR PR_LOG_ERROR
+# define ML_WARNING PR_LOG_WARNING
+# define ML_NOTICE PR_LOG_INFO
+# define ML_INFO PR_LOG_DEBUG
+# define ML_DEBUG PR_LOG_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 (PR_LOG_TEST(getLogModule(), level)) { \
+ std::stringstream str; \
+ str << b; \
+ PR_LOG(getLogModule(), level, ("%s", str.str().c_str())); \
+ } \
+ } while (0)
+#endif // MOZILLA_INTERNAL_API
+#endif // logging_h__
diff --git a/dom/media/webrtc/transport/m_cpp_utils.h b/dom/media/webrtc/transport/m_cpp_utils.h
new file mode 100644
index 0000000000..a1469f798a
--- /dev/null
+++ b/dom/media/webrtc/transport/m_cpp_utils.h
@@ -0,0 +1,25 @@
+/* -*- 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)
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/mdns_service/Cargo.toml b/dom/media/webrtc/transport/mdns_service/Cargo.toml
new file mode 100644
index 0000000000..ec7e182d4b
--- /dev/null
+++ b/dom/media/webrtc/transport/mdns_service/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "mdns_service"
+version = "0.1.1"
+authors = ["Dan Minor <dminor@mozilla.com>"]
+edition = "2018"
+license = "MPL-2.0"
+
+[dependencies]
+byteorder = "1.3.1"
+dns-parser = "0.8.0"
+gecko-profiler = { path = "../../../../../tools/profiler/rust-api" }
+log = "0.4"
+socket2 = { version = "0.4", features = ["all"] }
+uuid = { version = "1.0", features = ["v4"] }
diff --git a/dom/media/webrtc/transport/mdns_service/mdns_service.h b/dom/media/webrtc/transport/mdns_service/mdns_service.h
new file mode 100644
index 0000000000..5e8c252ebc
--- /dev/null
+++ b/dom/media/webrtc/transport/mdns_service/mdns_service.h
@@ -0,0 +1,28 @@
+/* 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 <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <new>
+
+struct MDNSService;
+
+extern "C" {
+
+void mdns_service_register_hostname(MDNSService* serv, const char* hostname,
+ const char* addr);
+
+MDNSService* mdns_service_start(const char* ifaddr);
+
+void mdns_service_stop(MDNSService* serv);
+
+void mdns_service_query_hostname(
+ MDNSService* serv, void* data,
+ void (*resolved)(void* data, const char* hostname, const char* address),
+ void (*timedout)(void* data, const char* hostname), const char* hostname);
+
+void mdns_service_unregister_hostname(MDNSService* serv, const char* hostname);
+
+} // extern "C"
diff --git a/dom/media/webrtc/transport/mdns_service/src/lib.rs b/dom/media/webrtc/transport/mdns_service/src/lib.rs
new file mode 100644
index 0000000000..fbd07b45f8
--- /dev/null
+++ b/dom/media/webrtc/transport/mdns_service/src/lib.rs
@@ -0,0 +1,843 @@
+/* -*- Mode: rust; rust-indent-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/. */
+use byteorder::{BigEndian, WriteBytesExt};
+use socket2::{Domain, Socket, Type};
+use std::collections::HashMap;
+use std::collections::LinkedList;
+use std::ffi::{c_void, CStr, CString};
+use std::io;
+use std::net;
+use std::os::raw::c_char;
+use std::sync::mpsc::channel;
+use std::thread;
+use std::time;
+use uuid::Uuid;
+
+#[macro_use]
+extern crate log;
+
+struct Callback {
+ data: *const c_void,
+ resolved: unsafe extern "C" fn(*const c_void, *const c_char, *const c_char),
+ timedout: unsafe extern "C" fn(*const c_void, *const c_char),
+}
+
+unsafe impl Send for Callback {}
+
+fn hostname_resolved(callback: &Callback, hostname: &str, addr: &str) {
+ if let Ok(hostname) = CString::new(hostname) {
+ if let Ok(addr) = CString::new(addr) {
+ unsafe {
+ (callback.resolved)(callback.data, hostname.as_ptr(), addr.as_ptr());
+ }
+ }
+ }
+}
+
+fn hostname_timedout(callback: &Callback, hostname: &str) {
+ if let Ok(hostname) = CString::new(hostname) {
+ unsafe {
+ (callback.timedout)(callback.data, hostname.as_ptr());
+ }
+ }
+}
+
+// This code is derived from code for creating questions in the dns-parser
+// crate. It would be nice to upstream this, or something similar.
+fn create_answer(id: u16, answers: &[(String, &[u8])]) -> Result<Vec<u8>, io::Error> {
+ let mut buf = Vec::with_capacity(512);
+ let head = dns_parser::Header {
+ id,
+ query: false,
+ opcode: dns_parser::Opcode::StandardQuery,
+ authoritative: true,
+ truncated: false,
+ recursion_desired: false,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: dns_parser::ResponseCode::NoError,
+ questions: 0,
+ answers: answers.len() as u16,
+ nameservers: 0,
+ additional: 0,
+ };
+
+ buf.extend([0u8; 12].iter());
+ head.write(&mut buf[..12]);
+
+ for (name, addr) in answers {
+ for part in name.split('.') {
+ if part.len() > 62 {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "Name part length too long",
+ ));
+ }
+ let ln = part.len() as u8;
+ buf.push(ln);
+ buf.extend(part.as_bytes());
+ }
+ buf.push(0);
+
+ if addr.len() == 4 {
+ buf.write_u16::<BigEndian>(dns_parser::Type::A as u16)?;
+ } else {
+ buf.write_u16::<BigEndian>(dns_parser::Type::AAAA as u16)?;
+ }
+ // set cache flush bit
+ buf.write_u16::<BigEndian>(dns_parser::Class::IN as u16 | (0x1 << 15))?;
+ buf.write_u32::<BigEndian>(120)?;
+ buf.write_u16::<BigEndian>(addr.len() as u16)?;
+ buf.extend(*addr);
+ }
+
+ Ok(buf)
+}
+
+fn create_query(id: u16, queries: &[String]) -> Result<Vec<u8>, io::Error> {
+ let mut buf = Vec::with_capacity(512);
+ let head = dns_parser::Header {
+ id,
+ query: true,
+ opcode: dns_parser::Opcode::StandardQuery,
+ authoritative: false,
+ truncated: false,
+ recursion_desired: false,
+ recursion_available: false,
+ authenticated_data: false,
+ checking_disabled: false,
+ response_code: dns_parser::ResponseCode::NoError,
+ questions: queries.len() as u16,
+ answers: 0,
+ nameservers: 0,
+ additional: 0,
+ };
+
+ buf.extend([0u8; 12].iter());
+ head.write(&mut buf[..12]);
+
+ for name in queries {
+ for part in name.split('.') {
+ assert!(part.len() < 63);
+ let ln = part.len() as u8;
+ buf.push(ln);
+ buf.extend(part.as_bytes());
+ }
+ buf.push(0);
+
+ buf.write_u16::<BigEndian>(dns_parser::QueryType::A as u16)?;
+ buf.write_u16::<BigEndian>(dns_parser::QueryClass::IN as u16)?;
+ }
+
+ Ok(buf)
+}
+
+fn handle_queries(
+ socket: &std::net::UdpSocket,
+ mdns_addr: &std::net::SocketAddr,
+ pending_queries: &mut HashMap<String, Query>,
+ unsent_queries: &mut LinkedList<Query>,
+) {
+ if pending_queries.len() < 50 {
+ let mut queries: Vec<Query> = Vec::new();
+ while queries.len() < 5 && !unsent_queries.is_empty() {
+ if let Some(query) = unsent_queries.pop_front() {
+ if !pending_queries.contains_key(&query.hostname) {
+ queries.push(query);
+ }
+ }
+ }
+ if !queries.is_empty() {
+ let query_hostnames: Vec<String> =
+ queries.iter().map(|q| q.hostname.to_string()).collect();
+
+ if let Ok(buf) = create_query(0, &query_hostnames) {
+ match socket.send_to(&buf, &mdns_addr) {
+ Ok(_) => {
+ for query in queries {
+ pending_queries.insert(query.hostname.to_string(), query);
+ }
+ }
+ Err(err) => {
+ warn!("Sending mDNS query failed: {}", err);
+ if err.kind() != io::ErrorKind::PermissionDenied {
+ for query in queries {
+ unsent_queries.push_back(query);
+ }
+ } else {
+ for query in queries {
+ hostname_timedout(&query.callback, &query.hostname);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ let now = time::Instant::now();
+ let expired: Vec<String> = pending_queries
+ .iter()
+ .filter(|(_, query)| now.duration_since(query.timestamp).as_secs() >= 3)
+ .map(|(hostname, _)| hostname.to_string())
+ .collect();
+ for hostname in expired {
+ if let Some(mut query) = pending_queries.remove(&hostname) {
+ query.attempts += 1;
+ if query.attempts < 3 {
+ query.timestamp = now;
+ unsent_queries.push_back(query);
+ } else {
+ hostname_timedout(&query.callback, &hostname);
+ }
+ }
+ }
+}
+
+fn handle_mdns_socket(
+ socket: &std::net::UdpSocket,
+ mdns_addr: &std::net::SocketAddr,
+ mut buffer: &mut [u8],
+ hosts: &mut HashMap<String, Vec<u8>>,
+ pending_queries: &mut HashMap<String, Query>,
+) -> bool {
+ // Record a simple marker to see how often this is called.
+ gecko_profiler::add_untyped_marker(
+ "handle_mdns_socket",
+ gecko_profiler::gecko_profiler_category!(Network),
+ Default::default(),
+ );
+
+ match socket.recv_from(&mut buffer) {
+ Ok((amt, _)) => {
+ if amt > 0 {
+ let buffer = &buffer[0..amt];
+ match dns_parser::Packet::parse(&buffer) {
+ Ok(parsed) => {
+ let mut answers: Vec<(String, &[u8])> = Vec::new();
+
+ // If a packet contains both both questions and
+ // answers, the questions should be ignored.
+ if parsed.answers.is_empty() {
+ parsed
+ .questions
+ .iter()
+ .filter(|question| question.qtype == dns_parser::QueryType::A)
+ .for_each(|question| {
+ let qname = question.qname.to_string();
+ trace!("mDNS question: {} {:?}", qname, question.qtype);
+ if let Some(octets) = hosts.get(&qname) {
+ trace!("Sending mDNS answer for {}: {:?}", qname, octets);
+ answers.push((qname, &octets));
+ }
+ });
+ }
+ for answer in parsed.answers {
+ let hostname = answer.name.to_string();
+ match pending_queries.get(&hostname) {
+ Some(query) => {
+ match answer.data {
+ dns_parser::RData::A(dns_parser::rdata::a::Record(
+ addr,
+ )) => {
+ let addr = addr.to_string();
+ trace!("mDNS response: {} {}", hostname, addr);
+ hostname_resolved(&query.callback, &hostname, &addr);
+ }
+ dns_parser::RData::AAAA(
+ dns_parser::rdata::aaaa::Record(addr),
+ ) => {
+ let addr = addr.to_string();
+ trace!("mDNS response: {} {}", hostname, addr);
+ hostname_resolved(&query.callback, &hostname, &addr);
+ }
+ _ => {}
+ }
+ pending_queries.remove(&hostname);
+ }
+ None => {
+ continue;
+ }
+ }
+ }
+ // TODO: If we did not answer every query in this
+ // question, we should wait for a random amount of time
+ // so as to not collide with someone else responding to
+ // this query.
+ if !answers.is_empty() {
+ if let Ok(buf) = create_answer(parsed.header.id, &answers) {
+ if let Err(err) = socket.send_to(&buf, &mdns_addr) {
+ warn!("Sending mDNS answer failed: {}", err);
+ }
+ }
+ }
+ }
+ Err(err) => {
+ warn!("Could not parse mDNS packet: {}", err);
+ }
+ }
+ }
+ }
+ Err(err) => {
+ if err.kind() != io::ErrorKind::Interrupted
+ && err.kind() != io::ErrorKind::TimedOut
+ && err.kind() != io::ErrorKind::WouldBlock
+ {
+ error!("Socket error: {}", err);
+ return false;
+ }
+ }
+ }
+
+ true
+}
+
+fn validate_hostname(hostname: &str) -> bool {
+ match hostname.find(".local") {
+ Some(index) => match hostname.get(0..index) {
+ Some(uuid) => match uuid.get(0..36) {
+ Some(initial) => match Uuid::parse_str(initial) {
+ Ok(_) => {
+ // Oddly enough, Safari does not generate valid UUIDs,
+ // the last part sometimes contains more than 12 digits.
+ match uuid.get(36..) {
+ Some(trailing) => {
+ for c in trailing.chars() {
+ if !c.is_ascii_hexdigit() {
+ return false;
+ }
+ }
+ true
+ }
+ None => true,
+ }
+ }
+ Err(_) => false,
+ },
+ None => false,
+ },
+ None => false,
+ },
+ None => false,
+ }
+}
+
+enum ServiceControl {
+ Register {
+ hostname: String,
+ address: String,
+ },
+ Query {
+ callback: Callback,
+ hostname: String,
+ },
+ Unregister {
+ hostname: String,
+ },
+ Stop,
+}
+
+struct Query {
+ hostname: String,
+ callback: Callback,
+ timestamp: time::Instant,
+ attempts: i32,
+}
+
+impl Query {
+ fn new(hostname: &str, callback: Callback) -> Query {
+ Query {
+ hostname: hostname.to_string(),
+ callback,
+ timestamp: time::Instant::now(),
+ attempts: 0,
+ }
+ }
+}
+
+pub struct MDNSService {
+ handle: Option<std::thread::JoinHandle<()>>,
+ sender: Option<std::sync::mpsc::Sender<ServiceControl>>,
+}
+
+impl MDNSService {
+ fn register_hostname(&mut self, hostname: &str, address: &str) {
+ if let Some(sender) = &self.sender {
+ if let Err(err) = sender.send(ServiceControl::Register {
+ hostname: hostname.to_string(),
+ address: address.to_string(),
+ }) {
+ warn!(
+ "Could not send register hostname {} message: {}",
+ hostname, err
+ );
+ }
+ }
+ }
+
+ fn query_hostname(&mut self, callback: Callback, hostname: &str) {
+ if let Some(sender) = &self.sender {
+ if let Err(err) = sender.send(ServiceControl::Query {
+ callback,
+ hostname: hostname.to_string(),
+ }) {
+ warn!(
+ "Could not send query hostname {} message: {}",
+ hostname, err
+ );
+ }
+ }
+ }
+
+ fn unregister_hostname(&mut self, hostname: &str) {
+ if let Some(sender) = &self.sender {
+ if let Err(err) = sender.send(ServiceControl::Unregister {
+ hostname: hostname.to_string(),
+ }) {
+ warn!(
+ "Could not send unregister hostname {} message: {}",
+ hostname, err
+ );
+ }
+ }
+ }
+
+ fn start(&mut self, addrs: Vec<std::net::Ipv4Addr>) -> io::Result<()> {
+ let (sender, receiver) = channel();
+ self.sender = Some(sender);
+
+ let mdns_addr = std::net::Ipv4Addr::new(224, 0, 0, 251);
+ let port = 5353;
+
+ let socket = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
+ socket.set_reuse_address(true)?;
+
+ #[cfg(not(target_os = "windows"))]
+ socket.set_reuse_port(true)?;
+ socket.bind(&socket2::SockAddr::from(std::net::SocketAddr::from((
+ [0, 0, 0, 0],
+ port,
+ ))))?;
+
+ let socket = std::net::UdpSocket::from(socket);
+ socket.set_multicast_loop_v4(true)?;
+ socket.set_read_timeout(Some(time::Duration::from_millis(1)))?;
+ socket.set_write_timeout(Some(time::Duration::from_millis(1)))?;
+ for addr in addrs {
+ if let Err(err) = socket.join_multicast_v4(&mdns_addr, &addr) {
+ warn!(
+ "Could not join multicast group on interface: {:?}: {}",
+ addr, err
+ );
+ }
+ }
+
+ let thread_name = "mdns_service";
+ let builder = thread::Builder::new().name(thread_name.into());
+ self.handle = Some(builder.spawn(move || {
+ gecko_profiler::register_thread(thread_name);
+ let mdns_addr = std::net::SocketAddr::from(([224, 0, 0, 251], port));
+ let mut buffer: [u8; 9_000] = [0; 9_000];
+ let mut hosts = HashMap::new();
+ let mut unsent_queries = LinkedList::new();
+ let mut pending_queries = HashMap::new();
+ loop {
+ match receiver.try_recv() {
+ Ok(msg) => match msg {
+ ServiceControl::Register { hostname, address } => {
+ if !validate_hostname(&hostname) {
+ warn!("Not registering invalid hostname: {}", hostname);
+ continue;
+ }
+ trace!("Registering {} for: {}", hostname, address);
+ match address.parse().and_then(|ip| {
+ Ok(match ip {
+ net::IpAddr::V4(ip) => ip.octets().to_vec(),
+ net::IpAddr::V6(ip) => ip.octets().to_vec(),
+ })
+ }) {
+ Ok(octets) => {
+ let mut v = Vec::new();
+ v.extend(octets);
+ hosts.insert(hostname, v);
+ }
+ Err(err) => {
+ warn!(
+ "Could not parse address for {}: {}: {}",
+ hostname, address, err
+ );
+ }
+ }
+ }
+ ServiceControl::Query { callback, hostname } => {
+ trace!("Querying {}", hostname);
+ if !validate_hostname(&hostname) {
+ warn!("Not sending mDNS query for invalid hostname: {}", hostname);
+ continue;
+ }
+ unsent_queries.push_back(Query::new(&hostname, callback));
+ }
+ ServiceControl::Unregister { hostname } => {
+ trace!("Unregistering {}", hostname);
+ hosts.remove(&hostname);
+ }
+ ServiceControl::Stop => {
+ trace!("Stopping");
+ break;
+ }
+ },
+ Err(std::sync::mpsc::TryRecvError::Disconnected) => {
+ break;
+ }
+ Err(std::sync::mpsc::TryRecvError::Empty) => {}
+ }
+
+ handle_queries(
+ &socket,
+ &mdns_addr,
+ &mut pending_queries,
+ &mut unsent_queries,
+ );
+
+ if !handle_mdns_socket(
+ &socket,
+ &mdns_addr,
+ &mut buffer,
+ &mut hosts,
+ &mut pending_queries,
+ ) {
+ break;
+ }
+ }
+ gecko_profiler::unregister_thread();
+ })?);
+
+ Ok(())
+ }
+
+ fn stop(self) {
+ if let Some(sender) = self.sender {
+ if let Err(err) = sender.send(ServiceControl::Stop) {
+ warn!("Could not stop mDNS Service: {}", err);
+ }
+ if let Some(handle) = self.handle {
+ if handle.join().is_err() {
+ error!("Error on thread join");
+ }
+ }
+ }
+ }
+
+ fn new() -> MDNSService {
+ MDNSService {
+ handle: None,
+ sender: None,
+ }
+ }
+}
+
+/// # Safety
+///
+/// This function must only be called with a valid MDNSService pointer.
+/// This hostname and address arguments must be zero terminated strings.
+#[no_mangle]
+pub unsafe extern "C" fn mdns_service_register_hostname(
+ serv: *mut MDNSService,
+ hostname: *const c_char,
+ address: *const c_char,
+) {
+ assert!(!serv.is_null());
+ assert!(!hostname.is_null());
+ assert!(!address.is_null());
+ let hostname = CStr::from_ptr(hostname).to_string_lossy();
+ let address = CStr::from_ptr(address).to_string_lossy();
+ (*serv).register_hostname(&hostname, &address);
+}
+
+/// # Safety
+///
+/// This ifaddrs argument must be a zero terminated string.
+#[no_mangle]
+pub unsafe extern "C" fn mdns_service_start(ifaddrs: *const c_char) -> *mut MDNSService {
+ assert!(!ifaddrs.is_null());
+ let mut r = Box::new(MDNSService::new());
+ let ifaddrs = CStr::from_ptr(ifaddrs).to_string_lossy();
+ let addrs: Vec<std::net::Ipv4Addr> =
+ ifaddrs.split(';').filter_map(|x| x.parse().ok()).collect();
+
+ if addrs.is_empty() {
+ warn!("Could not parse interface addresses from: {}", ifaddrs);
+ } else if let Err(err) = r.start(addrs) {
+ warn!("Could not start mDNS Service: {}", err);
+ }
+
+ Box::into_raw(r)
+}
+
+/// # Safety
+///
+/// This function must only be called with a valid MDNSService pointer.
+#[no_mangle]
+pub unsafe extern "C" fn mdns_service_stop(serv: *mut MDNSService) {
+ assert!(!serv.is_null());
+ let boxed = Box::from_raw(serv);
+ boxed.stop();
+}
+
+/// # Safety
+///
+/// This function must only be called with a valid MDNSService pointer.
+/// The data argument will be passed back into the resolved and timedout
+/// functions. The object it points to must not be freed until the MDNSService
+/// has stopped.
+#[no_mangle]
+pub unsafe extern "C" fn mdns_service_query_hostname(
+ serv: *mut MDNSService,
+ data: *const c_void,
+ resolved: unsafe extern "C" fn(*const c_void, *const c_char, *const c_char),
+ timedout: unsafe extern "C" fn(*const c_void, *const c_char),
+ hostname: *const c_char,
+) {
+ assert!(!serv.is_null());
+ assert!(!data.is_null());
+ assert!(!hostname.is_null());
+ let hostname = CStr::from_ptr(hostname).to_string_lossy();
+ let callback = Callback {
+ data,
+ resolved,
+ timedout,
+ };
+ (*serv).query_hostname(callback, &hostname);
+}
+
+/// # Safety
+///
+/// This function must only be called with a valid MDNSService pointer.
+/// This function should only be called once per hostname.
+#[no_mangle]
+pub unsafe extern "C" fn mdns_service_unregister_hostname(
+ serv: *mut MDNSService,
+ hostname: *const c_char,
+) {
+ assert!(!serv.is_null());
+ assert!(!hostname.is_null());
+ let hostname = CStr::from_ptr(hostname).to_string_lossy();
+ (*serv).unregister_hostname(&hostname);
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::create_query;
+ use crate::validate_hostname;
+ use crate::Callback;
+ use crate::MDNSService;
+ use socket2::{Domain, Socket, Type};
+ use std::collections::HashSet;
+ use std::ffi::c_void;
+ use std::io;
+ use std::iter::FromIterator;
+ use std::os::raw::c_char;
+ use std::thread;
+ use std::time;
+ use uuid::Uuid;
+
+ #[no_mangle]
+ pub unsafe extern "C" fn mdns_service_resolved(
+ _: *const c_void,
+ _: *const c_char,
+ _: *const c_char,
+ ) -> () {
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn mdns_service_timedout(_: *const c_void, _: *const c_char) -> () {}
+
+ fn listen_until(addr: &std::net::Ipv4Addr, stop: u64) -> thread::JoinHandle<Vec<String>> {
+ let port = 5353;
+
+ let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap();
+ socket.set_reuse_address(true).unwrap();
+
+ #[cfg(not(target_os = "windows"))]
+ socket.set_reuse_port(true).unwrap();
+ socket
+ .bind(&socket2::SockAddr::from(std::net::SocketAddr::from((
+ [0, 0, 0, 0],
+ port,
+ ))))
+ .unwrap();
+
+ let socket = std::net::UdpSocket::from(socket);
+ socket.set_multicast_loop_v4(true).unwrap();
+ socket
+ .set_read_timeout(Some(time::Duration::from_millis(10)))
+ .unwrap();
+ socket
+ .set_write_timeout(Some(time::Duration::from_millis(10)))
+ .unwrap();
+ socket
+ .join_multicast_v4(&std::net::Ipv4Addr::new(224, 0, 0, 251), &addr)
+ .unwrap();
+
+ let mut buffer: [u8; 9_000] = [0; 9_000];
+ thread::spawn(move || {
+ let start = time::Instant::now();
+ let mut questions = Vec::new();
+ while time::Instant::now().duration_since(start).as_secs() < stop {
+ match socket.recv_from(&mut buffer) {
+ Ok((amt, _)) => {
+ if amt > 0 {
+ let buffer = &buffer[0..amt];
+ match dns_parser::Packet::parse(&buffer) {
+ Ok(parsed) => {
+ parsed
+ .questions
+ .iter()
+ .filter(|question| {
+ question.qtype == dns_parser::QueryType::A
+ })
+ .for_each(|question| {
+ let qname = question.qname.to_string();
+ questions.push(qname);
+ });
+ }
+ Err(err) => {
+ warn!("Could not parse mDNS packet: {}", err);
+ }
+ }
+ }
+ }
+ Err(err) => {
+ if err.kind() != io::ErrorKind::WouldBlock
+ && err.kind() != io::ErrorKind::TimedOut
+ {
+ error!("Socket error: {}", err);
+ break;
+ }
+ }
+ }
+ }
+ questions
+ })
+ }
+
+ #[test]
+ fn test_validate_hostname() {
+ assert_eq!(
+ validate_hostname("e17f08d4-689a-4df6-ba31-35bb9f041100.local"),
+ true
+ );
+ assert_eq!(
+ validate_hostname("62240723-ae6d-4f6a-99b8-94a233e3f84a2.local"),
+ true
+ );
+ assert_eq!(
+ validate_hostname("62240723-ae6d-4f6a-99b8.94e3f84a2.local"),
+ false
+ );
+ assert_eq!(validate_hostname("hi there"), false);
+ }
+
+ #[test]
+ fn start_stop() {
+ let mut service = MDNSService::new();
+ let addr = "127.0.0.1".parse().unwrap();
+ service.start(vec![addr]).unwrap();
+ service.stop();
+ }
+
+ #[test]
+ fn simple_query() {
+ let mut service = MDNSService::new();
+ let addr = "127.0.0.1".parse().unwrap();
+ let handle = listen_until(&addr, 1);
+
+ service.start(vec![addr]).unwrap();
+
+ let callback = Callback {
+ data: 0 as *const c_void,
+ resolved: mdns_service_resolved,
+ timedout: mdns_service_timedout,
+ };
+ let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local";
+ service.query_hostname(callback, &hostname);
+ service.stop();
+ let questions = handle.join().unwrap();
+ assert!(questions.contains(&hostname));
+ }
+
+ #[test]
+ fn rate_limited_query() {
+ let mut service = MDNSService::new();
+ let addr = "127.0.0.1".parse().unwrap();
+ let handle = listen_until(&addr, 1);
+
+ service.start(vec![addr]).unwrap();
+
+ let mut hostnames = HashSet::new();
+ for _ in 0..100 {
+ let callback = Callback {
+ data: 0 as *const c_void,
+ resolved: mdns_service_resolved,
+ timedout: mdns_service_timedout,
+ };
+ let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local";
+ service.query_hostname(callback, &hostname);
+ hostnames.insert(hostname);
+ }
+ service.stop();
+ let questions = HashSet::from_iter(handle.join().unwrap().iter().map(|x| x.to_string()));
+ let intersection: HashSet<&String> = questions.intersection(&hostnames).collect();
+ assert_eq!(intersection.len(), 50);
+ }
+
+ #[test]
+ fn repeat_failed_query() {
+ let mut service = MDNSService::new();
+ let addr = "127.0.0.1".parse().unwrap();
+ let handle = listen_until(&addr, 4);
+
+ service.start(vec![addr]).unwrap();
+
+ let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local";
+ let callback = Callback {
+ data: 0 as *const c_void,
+ resolved: mdns_service_resolved,
+ timedout: mdns_service_timedout,
+ };
+ service.query_hostname(callback, &hostname);
+ thread::sleep(time::Duration::from_secs(4));
+ service.stop();
+
+ let questions: Vec<String> = handle
+ .join()
+ .unwrap()
+ .iter()
+ .filter(|x| *x == &hostname)
+ .map(|x| x.to_string())
+ .collect();
+ assert_eq!(questions.len(), 2);
+ }
+
+ #[test]
+ fn multiple_queries_in_a_single_packet() {
+ let mut hostnames: Vec<String> = Vec::new();
+ for _ in 0..100 {
+ let hostname = Uuid::new_v4().as_hyphenated().to_string() + ".local";
+ hostnames.push(hostname);
+ }
+
+ match create_query(42, &hostnames) {
+ Ok(q) => match dns_parser::Packet::parse(&q) {
+ Ok(parsed) => {
+ assert_eq!(parsed.questions.len(), 100);
+ }
+ Err(_) => assert!(false),
+ },
+ Err(_) => assert!(false),
+ }
+ }
+}
diff --git a/dom/media/webrtc/transport/mediapacket.cpp b/dom/media/webrtc/transport/mediapacket.cpp
new file mode 100644
index 0000000000..38ab65ed2a
--- /dev/null
+++ b/dom/media/webrtc/transport/mediapacket.cpp
@@ -0,0 +1,145 @@
+/* -*- 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 "mediapacket.h"
+
+#include <cstring>
+#include "ipc/IPCMessageUtils.h"
+
+namespace mozilla {
+
+void MediaPacket::Copy(const uint8_t* data, size_t len, size_t capacity) {
+ if (capacity < len) {
+ capacity = len;
+ }
+ data_.reset(new uint8_t[capacity]);
+ len_ = len;
+ capacity_ = capacity;
+ memcpy(data_.get(), data, len);
+}
+
+MediaPacket MediaPacket::Clone() const {
+ MediaPacket newPacket;
+ newPacket.type_ = type_;
+ newPacket.sdp_level_ = sdp_level_;
+ newPacket.Copy(data_.get(), len_, capacity_);
+ return newPacket;
+}
+
+void MediaPacket::Serialize(IPC::MessageWriter* aWriter) const {
+ aWriter->WriteUInt32(len_);
+ aWriter->WriteUInt32(capacity_);
+ if (len_) {
+ aWriter->WriteBytes(data_.get(), len_);
+ }
+ aWriter->WriteUInt32(encrypted_len_);
+ if (encrypted_len_) {
+ aWriter->WriteBytes(encrypted_data_.get(), encrypted_len_);
+ }
+ aWriter->WriteInt32(sdp_level_.isSome() ? *sdp_level_ : -1);
+ aWriter->WriteInt32(type_);
+}
+
+bool MediaPacket::Deserialize(IPC::MessageReader* aReader) {
+ Reset();
+ uint32_t len;
+ if (!aReader->ReadUInt32(&len)) {
+ return false;
+ }
+ uint32_t capacity;
+ if (!aReader->ReadUInt32(&capacity)) {
+ return false;
+ }
+ if (len) {
+ MOZ_RELEASE_ASSERT(capacity >= len);
+ UniquePtr<uint8_t[]> data(new uint8_t[capacity]);
+ if (!aReader->ReadBytesInto(data.get(), len)) {
+ return false;
+ }
+ data_ = std::move(data);
+ len_ = len;
+ capacity_ = capacity;
+ }
+
+ if (!aReader->ReadUInt32(&len)) {
+ return false;
+ }
+ if (len) {
+ UniquePtr<uint8_t[]> data(new uint8_t[len]);
+ if (!aReader->ReadBytesInto(data.get(), len)) {
+ return false;
+ }
+ encrypted_data_ = std::move(data);
+ encrypted_len_ = len;
+ }
+
+ int32_t sdp_level;
+ if (!aReader->ReadInt32(&sdp_level)) {
+ return false;
+ }
+
+ if (sdp_level >= 0) {
+ sdp_level_ = Some(sdp_level);
+ }
+
+ int32_t type;
+ if (!aReader->ReadInt32(&type)) {
+ return false;
+ }
+ type_ = static_cast<Type>(type);
+ return true;
+}
+
+static bool IsRtp(const uint8_t* data, size_t len) {
+ if (len < 2) return false;
+
+ // Check if this is a RTCP packet. Logic based on the types listed in
+ // media/webrtc/trunk/src/modules/rtp_rtcp/source/rtp_utility.cc
+
+ // Anything outside this range is RTP.
+ if ((data[1] < 192) || (data[1] > 207)) return true;
+
+ if (data[1] == 192) // FIR
+ return false;
+
+ if (data[1] == 193) // NACK, but could also be RTP. This makes us sad
+ return true; // but it's how webrtc.org behaves.
+
+ if (data[1] == 194) return true;
+
+ if (data[1] == 195) // IJ.
+ return false;
+
+ if ((data[1] > 195) && (data[1] < 200)) // the > 195 is redundant
+ return true;
+
+ if ((data[1] >= 200) && (data[1] <= 207)) // SR, RR, SDES, BYE,
+ return false; // APP, RTPFB, PSFB, XR
+
+ MOZ_ASSERT(false); // Not reached, belt and suspenders.
+ return true;
+}
+
+void MediaPacket::Categorize() {
+ SetType(MediaPacket::UNCLASSIFIED);
+
+ if (!data_ || len_ < 4) {
+ return;
+ }
+
+ if (data_[0] >= 20 && data_[0] <= 63) {
+ // DTLS per RFC 7983
+ SetType(MediaPacket::DTLS);
+ } else if (data_[0] > 127 && data_[0] < 192) {
+ // RTP/RTCP per RFC 7983
+ if (IsRtp(data_.get(), len_)) {
+ SetType(MediaPacket::SRTP);
+ } else {
+ SetType(MediaPacket::SRTCP);
+ }
+ }
+}
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/mediapacket.h b/dom/media/webrtc/transport/mediapacket.h
new file mode 100644
index 0000000000..563056c4a4
--- /dev/null
+++ b/dom/media/webrtc/transport/mediapacket.h
@@ -0,0 +1,117 @@
+/* -*- 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 mediapacket_h__
+#define mediapacket_h__
+
+#include <cstddef>
+#include <cstdint>
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Maybe.h"
+
+class PickleIterator;
+
+namespace IPC {
+class Message;
+class MessageReader;
+class MessageWriter;
+} // namespace IPC
+
+namespace mozilla {
+
+// TODO: It might be worthwhile to teach this class how to "borrow" a buffer.
+// That would make it easier to misuse, however, so maybe not worth it.
+class MediaPacket {
+ public:
+ MediaPacket() = default;
+ MediaPacket(MediaPacket&& orig) = default;
+ MediaPacket& operator=(MediaPacket&& orig) = default;
+
+ MediaPacket Clone() const;
+
+ // Takes ownership of the passed-in data
+ void Take(UniquePtr<uint8_t[]>&& data, size_t len, size_t capacity = 0) {
+ data_ = std::move(data);
+ len_ = len;
+ if (capacity < len) {
+ capacity = len;
+ }
+ capacity_ = capacity;
+ }
+
+ void Reset() {
+ data_.reset();
+ len_ = 0;
+ capacity_ = 0;
+ encrypted_data_.reset();
+ encrypted_len_ = 0;
+ sdp_level_.reset();
+ }
+
+ // Copies the passed-in data
+ void Copy(const uint8_t* data, size_t len, size_t capacity = 0);
+
+ uint8_t* data() const { return data_.get(); }
+
+ size_t len() const { return len_; }
+
+ void SetLength(size_t length) { len_ = length; }
+
+ size_t capacity() const { return capacity_; }
+
+ Maybe<size_t>& sdp_level() { return sdp_level_; }
+
+ void CopyDataToEncrypted() {
+ encrypted_data_ = std::move(data_);
+ encrypted_len_ = len_;
+ Copy(encrypted_data_.get(), len_);
+ }
+
+ const uint8_t* encrypted_data() const { return encrypted_data_.get(); }
+
+ size_t encrypted_len() const { return encrypted_len_; }
+
+ enum Type { UNCLASSIFIED, SRTP, SRTCP, DTLS, RTP, RTCP, SCTP };
+
+ void Categorize();
+
+ void SetType(Type type) { type_ = type; }
+
+ Type type() const { return type_; }
+
+ void Serialize(IPC::MessageWriter* aWriter) const;
+ bool Deserialize(IPC::MessageReader* aReader);
+
+ private:
+ UniquePtr<uint8_t[]> data_;
+ size_t len_ = 0;
+ size_t capacity_ = 0;
+ // Encrypted form of the data, if there is one.
+ UniquePtr<uint8_t[]> encrypted_data_;
+ size_t encrypted_len_ = 0;
+ // SDP level that this packet belongs to, if known.
+ Maybe<size_t> sdp_level_;
+ Type type_ = UNCLASSIFIED;
+};
+} // namespace mozilla
+
+namespace IPC {
+template <typename>
+struct ParamTraits;
+
+template <>
+struct ParamTraits<mozilla::MediaPacket> {
+ static void Write(MessageWriter* aWriter,
+ const mozilla::MediaPacket& aParam) {
+ aParam.Serialize(aWriter);
+ }
+
+ static bool Read(MessageReader* aReader, mozilla::MediaPacket* aResult) {
+ return aResult->Deserialize(aReader);
+ }
+};
+} // namespace IPC
+#endif // mediapacket_h__
diff --git a/dom/media/webrtc/transport/moz.build b/dom/media/webrtc/transport/moz.build
new file mode 100644
index 0000000000..fcf265e22f
--- /dev/null
+++ b/dom/media/webrtc/transport/moz.build
@@ -0,0 +1,23 @@
+# -*- 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "WebRTC: Networking")
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+DIRS += [
+ "/dom/media/webrtc/transport/third_party",
+ "/dom/media/webrtc/transport/build",
+ "/dom/media/webrtc/transport/ipc",
+ "/dom/media/webrtc/transport/srtp",
+]
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
+
+if CONFIG["FUZZING_INTERFACES"]:
+ TEST_DIRS += ["fuzztest"]
diff --git a/dom/media/webrtc/transport/nr_socket_proxy_config.cpp b/dom/media/webrtc/transport/nr_socket_proxy_config.cpp
new file mode 100644
index 0000000000..8c91a5d975
--- /dev/null
+++ b/dom/media/webrtc/transport/nr_socket_proxy_config.cpp
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "nr_socket_proxy_config.h"
+
+#include "mozilla/net/WebrtcProxyConfig.h"
+
+namespace mozilla {
+
+class NrSocketProxyConfig::Private {
+ public:
+ net::WebrtcProxyConfig mProxyConfig;
+};
+
+NrSocketProxyConfig::NrSocketProxyConfig(
+ const net::WebrtcProxyConfig& aProxyConfig)
+ : mPrivate(new Private({aProxyConfig})) {}
+
+NrSocketProxyConfig::NrSocketProxyConfig(NrSocketProxyConfig&& aOrig)
+ : mPrivate(std::move(aOrig.mPrivate)) {}
+
+NrSocketProxyConfig::~NrSocketProxyConfig() = default;
+
+const net::WebrtcProxyConfig& NrSocketProxyConfig::GetConfig() const {
+ return mPrivate->mProxyConfig;
+}
+
+bool NrSocketProxyConfig::GetForceProxy() const {
+ return mPrivate->mProxyConfig.forceProxy();
+}
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/nr_socket_proxy_config.h b/dom/media/webrtc/transport/nr_socket_proxy_config.h
new file mode 100644
index 0000000000..55ef4fdcb1
--- /dev/null
+++ b/dom/media/webrtc/transport/nr_socket_proxy_config.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 nr_socket_proxy_config__
+#define nr_socket_proxy_config__
+
+#include <memory>
+
+namespace mozilla {
+namespace net {
+class WebrtcProxyConfig;
+}
+
+class NrSocketProxyConfig {
+ public:
+ explicit NrSocketProxyConfig(const net::WebrtcProxyConfig& aProxyConfig);
+
+ // We need to actually write the default impl ourselves, because the compiler
+ // needs to know how to destroy mPrivate in case an exception is thrown, even
+ // though we disable exceptions in our build.
+ NrSocketProxyConfig(NrSocketProxyConfig&& aOrig);
+
+ ~NrSocketProxyConfig();
+
+ const net::WebrtcProxyConfig& GetConfig() const;
+ bool GetForceProxy() const;
+
+ private:
+ // dom::ProxyConfig includes stuff that conflicts with nICEr includes.
+ // Make it possible to include this header file without tripping over this
+ // problem.
+ class Private;
+ std::unique_ptr<Private> mPrivate;
+};
+
+} // namespace mozilla
+
+#endif // nr_socket_proxy_config__
diff --git a/dom/media/webrtc/transport/nr_socket_prsock.cpp b/dom/media/webrtc/transport/nr_socket_prsock.cpp
new file mode 100644
index 0000000000..5681d52ecd
--- /dev/null
+++ b/dom/media/webrtc/transport/nr_socket_prsock.cpp
@@ -0,0 +1,1785 @@
+/* -*- 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 "nsISocketFilter.h"
+#include "nsDebug.h"
+#include "nsNetUtil.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/UDPSocketChild.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"
+#include "nr_socket_tcp.h"
+#include "nr_socket_proxy_config.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");
+ if (mThread) {
+ // Likely a connection is somehow being held in CC or GC
+ NS_WARNING(
+ "SingletonThreads should be Released and shut down before exit!");
+ mThread->Shutdown();
+ mThread = nullptr;
+ }
+ }
+
+ DISALLOW_COPY_ASSIGN(SingletonThreadHolder);
+
+ public:
+ // Must be threadsafe for StaticRefPtr/ClearOnShutdown
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SingletonThreadHolder)
+
+ explicit SingletonThreadHolder(const nsACString& 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
+ */
+ void 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_NewNamedThread(mName, getter_AddRefs(mThread));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mThread,
+ "Should successfully create mtransport I/O thread");
+ r_log(LOG_GENERIC, LOG_DEBUG, "Created wrapped SingletonThread %p",
+ mThread.get());
+ }
+ r_log(LOG_GENERIC, LOG_DEBUG, "AddUse_i: %lu", (unsigned long)count);
+ }
+
+ void ReleaseUse() {
+ MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
+ nsrefcnt count = --mUseCount;
+ MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
+ if (mThread && 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->AsyncShutdown();
+ 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_i: %lu", (unsigned long)count);
+ }
+
+ private:
+ nsCString mName;
+ nsAutoRefCnt mUseCount;
+ nsCOMPtr<nsIThread> mParentThread;
+ nsCOMPtr<nsIThread> mThread;
+};
+
+static StaticRefPtr<SingletonThreadHolder> sThread;
+
+static void ClearSingletonOnShutdown() {
+ // We expect everybody to have done ReleaseUse() at the latest during
+ // xpcom-shutdown-threads. So we need to live longer than that.
+ ClearOnShutdown(&sThread, ShutdownPhase::XPCOMShutdownFinal);
+}
+#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("mtransport"_ns);
+ 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_QUERY_INTERFACE0(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(const 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(const 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(const 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:
+ // TODO: Rewrite this to use WebrtcTcpSocket.
+ // Also use the same logic for TLS.
+ if (my_addr_.fqdn[0] != '\0') ABORT(R_INTERNAL);
+
+ 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,
+ const 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;
+ cancel(NR_ASYNC_WAIT_READ);
+ cancel(NR_ASYNC_WAIT_WRITE);
+}
+
+int NrSocket::connect(const 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) {
+ r_log(LOG_GENERIC, LOG_CRIT, "PR_Connect failed: %d", PR_GetError());
+ 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 nsTArray<uint8_t>& data) {
+ return socket_->CallListenerReceivedData(host, port, data);
+}
+
+// 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() = default;
+
+void NrUdpSocketIpc::Destroy() {
+#if defined(MOZILLA_INTERNAL_API)
+ // destroy_i also dispatches back to STS to call ReleaseUse, to avoid shutting
+ // down the IO thread before close() runs.
+ // We use a NonOwning runnable because our refcount has already gone to 0.
+ io_thread_->Dispatch(
+ NewNonOwningRunnableMethod(__func__, this, &NrUdpSocketIpc::destroy_i));
+#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 nsTArray<uint8_t>& data) {
+ 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;
+ }
+ }
+
+ auto buf = MakeUnique<MediaPacket>();
+ buf->Copy(data.Elements(), data.Length());
+ RefPtr<nr_udp_message> msg(new nr_udp_message(addr, std::move(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 = socket_child_->LocalPort();
+
+ nsAutoCString address(socket_child_->LocalAddress());
+
+ 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;
+
+ MOZ_ASSERT(io_thread_);
+ 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,
+ const 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;
+ }
+
+ UniquePtr<MediaPacket> buf(new MediaPacket);
+ buf->Copy(static_cast<const uint8_t*>(msg), len);
+
+ RUN_ON_THREAD(
+ io_thread_,
+ mozilla::WrapRunnable(RefPtr<NrUdpSocketIpc>(this),
+ &NrUdpSocketIpc::sendto_i, addr, std::move(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_);
+
+ return nr_transport_addr_copy(addrp, &my_addr_);
+}
+
+int NrUdpSocketIpc::connect(const 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;
+ RefPtr<dom::UDPSocketChild> socketChild = new dom::UDPSocketChild();
+
+ // 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);
+ nsresult 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,
+ /* addressReuse = */ 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;
+ }
+
+ socket_child_->Connect(proxy, host, port);
+}
+
+void NrUdpSocketIpc::sendto_i(const net::NetAddr& addr,
+ UniquePtr<MediaPacket> 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)
+
+static void ReleaseIOThread_s() { sThread->ReleaseUse(); }
+
+void NrUdpSocketIpc::destroy_i() {
+ close_i();
+
+ RUN_ON_THREAD(sts_thread_, WrapRunnableNM(&ReleaseIOThread_s),
+ NS_DISPATCH_NORMAL);
+}
+#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);
+ }
+}
+
+} // namespace mozilla
+
+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, const 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* obj, const 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,
+ const std::shared_ptr<NrSocketProxyConfig>& config) {
+ int r, _status;
+
+ if (IsForbiddenAddress(addr)) {
+ ABORT(R_REJECTED);
+ }
+
+ if (config && config->GetForceProxy() && addr->protocol == IPPROTO_UDP) {
+ ABORT(R_REJECTED);
+ }
+
+ // create IPC bridge for content process
+ if (XRE_IsParentProcess()) {
+ // TODO: Make NrTcpSocket work on the parent process
+ *sock = new NrSocket();
+ } else if (XRE_IsSocketProcess()) {
+ if (addr->protocol == IPPROTO_TCP) {
+ *sock = new NrTcpSocket(config);
+ } else {
+ *sock = new NrSocket();
+ }
+ } else {
+ if (addr->protocol == IPPROTO_TCP) {
+ *sock = new NrTcpSocket(config);
+ } else {
+ *sock = new NrUdpSocketIpc();
+ }
+ }
+
+ r = (*sock)->create(addr);
+ if (r) ABORT(r);
+
+ _status = 0;
+abort:
+ if (_status) {
+ *sock = nullptr;
+ }
+ return _status;
+}
+
+// static
+bool NrSocketBase::IsForbiddenAddress(nr_transport_addr* addr) {
+ int r, port;
+
+ r = nr_transport_addr_get_port(addr, &port);
+ if (r) {
+ return true;
+ }
+
+ // allow auto assigned ports
+ if (port != 0) {
+ // Don't need to check an override scheme
+ nsresult rv = NS_CheckPortSafety(port, nullptr);
+ if (NS_FAILED(rv)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int nr_socket_local_destroy(void** objp) {
+ if (!objp || !*objp) return 0;
+
+ NrSocketBase* sock = static_cast<NrSocketBase*>(*objp);
+ *objp = nullptr;
+
+ 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, const 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, const 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/dom/media/webrtc/transport/nr_socket_prsock.h b/dom/media/webrtc/transport/nr_socket_prsock.h
new file mode 100644
index 0000000000..010fcb59bc
--- /dev/null
+++ b/dom/media/webrtc/transport/nr_socket_prsock.h
@@ -0,0 +1,320 @@
+/* -*- 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 <memory>
+#include <queue>
+
+#include "nspr.h"
+#include "prio.h"
+
+#include "nsCOMPtr.h"
+#include "nsASocketHandler.h"
+#include "nsXPCOM.h"
+#include "nsIEventTarget.h"
+#include "nsIUDPSocketChild.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+
+#include "mediapacket.h"
+#include "m_cpp_utils.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/ClearOnShutdown.h"
+
+// nICEr includes
+extern "C" {
+#include "transport_addr.h"
+#include "async_wait.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 {
+class NrSocketProxyConfig;
+} // namespace mozilla
+#endif
+
+namespace mozilla {
+
+namespace net {
+union NetAddr;
+}
+
+namespace dom {
+class UDPSocketChild;
+} // namespace dom
+
+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() = default;
+
+ // Factory method; will create either an NrSocket, NrUdpSocketIpc, or
+ // NrTcpSocketIpc as appropriate.
+ static int CreateSocket(nr_transport_addr* addr, RefPtr<NrSocketBase>* sock,
+ const std::shared_ptr<NrSocketProxyConfig>& config);
+ static bool IsForbiddenAddress(nr_transport_addr* addr);
+
+ // the nr_socket APIs
+ virtual int create(nr_transport_addr* addr) = 0;
+ virtual int sendto(const void* msg, size_t len, int flags,
+ const 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(const 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_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ 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_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(NrSocket, Destroy(),
+ override);
+ virtual void Destroy() { delete this; }
+
+ // 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,
+ const 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(const 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, UniquePtr<MediaPacket>&& data)
+ : from(from), data(std::move(data)) {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nr_udp_message);
+
+ PRNetAddr from;
+ UniquePtr<MediaPacket> data;
+
+ private:
+ ~nr_udp_message() = default;
+ 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() = default;
+
+ 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 nsTArray<uint8_t>& data);
+ 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,
+ const 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(const 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();
+ virtual void Destroy();
+
+ 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, UniquePtr<MediaPacket> buf);
+ void close_i();
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+ void destroy_i();
+#endif
+ // STS thread executor
+ void recv_callback_s(RefPtr<nr_udp_message> msg);
+
+ ReentrantMonitor monitor_ MOZ_UNANNOTATED; // protects err_and state_
+ bool err_;
+ NrSocketIpcState state_;
+
+ std::queue<RefPtr<nr_udp_message>> received_msgs_;
+
+ // only accessed from the io_thread
+ RefPtr<dom::UDPSocketChild> socket_child_;
+};
+
+// 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_;
+};
+
+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(const nr_transport_addr* addr,
+ nsACString* host, int32_t* port);
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/nr_socket_tcp.cpp b/dom/media/webrtc/transport/nr_socket_tcp.cpp
new file mode 100644
index 0000000000..a9cc5db312
--- /dev/null
+++ b/dom/media/webrtc/transport/nr_socket_tcp.cpp
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+/*
+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_socket_tcp.h"
+
+#include "mozilla/ErrorNames.h"
+
+#include "WebrtcTCPSocketWrapper.h"
+
+namespace mozilla {
+using namespace net;
+
+using std::shared_ptr;
+
+class NrTcpSocketData {
+ public:
+ explicit NrTcpSocketData(nsTArray<uint8_t>&& aData)
+ : mData(std::move(aData)) {
+ MOZ_COUNT_CTOR(NrTcpSocketData);
+ }
+
+ MOZ_COUNTED_DTOR(NrTcpSocketData)
+
+ const nsTArray<uint8_t>& GetData() const { return mData; }
+
+ private:
+ nsTArray<uint8_t> mData;
+};
+
+NrTcpSocket::NrTcpSocket(const shared_ptr<NrSocketProxyConfig>& aConfig)
+ : mClosed(false),
+ mReadOffset(0),
+ mConfig(aConfig),
+ mWebrtcTCPSocket(nullptr) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::NrTcpSocket %p\n", this);
+}
+
+NrTcpSocket::~NrTcpSocket() {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::~NrTcpSocket %p\n", this);
+ MOZ_ASSERT(!mWebrtcTCPSocket, "webrtc TCP socket not null");
+}
+
+int NrTcpSocket::create(nr_transport_addr* aAddr) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::create %p\n", this);
+ int32_t port;
+ nsCString host;
+
+ // Sanity check
+ if (nr_transport_addr_get_addrstring_and_port(aAddr, &host, &port)) {
+ return R_FAILED;
+ }
+
+ if (nr_transport_addr_copy(&my_addr_, aAddr)) {
+ return R_FAILED;
+ }
+
+ return 0;
+}
+
+int NrTcpSocket::connect(const nr_transport_addr* aAddr) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::connect %p\n", this);
+
+ nsCString remote_host;
+ int remote_port;
+
+ if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port(aAddr, &remote_host,
+ &remote_port))) {
+ return R_FAILED;
+ }
+
+ bool use_tls = aAddr->tls;
+
+ nsCString local_addr;
+ int local_port;
+
+ if (NS_WARN_IF(nr_transport_addr_get_addrstring_and_port(
+ &my_addr_, &local_addr, &local_port))) {
+ return R_FAILED;
+ }
+
+ mWebrtcTCPSocket = new WebrtcTCPSocketWrapper(this);
+
+ mWebrtcTCPSocket->AsyncOpen(remote_host, remote_port, local_addr, local_port,
+ use_tls, mConfig);
+
+ // trigger nr_socket_buffered to set write/read callback
+ return R_WOULDBLOCK;
+}
+
+void NrTcpSocket::close() {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::close %p\n", this);
+
+ if (mClosed) {
+ return;
+ }
+
+ mClosed = true;
+
+ // We're not always open at this point.
+ if (mWebrtcTCPSocket) {
+ mWebrtcTCPSocket->Close();
+ mWebrtcTCPSocket = nullptr;
+ }
+}
+
+int NrTcpSocket::write(const void* aBuffer, size_t aCount, size_t* aWrote) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::write %p count=%zu\n", this,
+ aCount);
+
+ if (mClosed) {
+ return R_FAILED;
+ }
+
+ if (!aWrote) {
+ return R_FAILED;
+ }
+
+ if (NS_WARN_IF(!mWebrtcTCPSocket)) {
+ return R_FAILED;
+ }
+
+ *aWrote = aCount;
+
+ if (aCount > 0) {
+ nsTArray<uint8_t> writeData;
+ writeData.SetLength(aCount);
+ memcpy(writeData.Elements(), aBuffer, aCount);
+
+ mWebrtcTCPSocket->SendWrite(std::move(writeData));
+ }
+
+ return 0;
+}
+
+int NrTcpSocket::read(void* aBuffer, size_t aCount, size_t* aRead) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::read %p\n", this);
+
+ if (mClosed) {
+ return R_FAILED;
+ }
+
+ if (!aRead) {
+ return R_FAILED;
+ }
+
+ *aRead = 0;
+
+ if (mReadQueue.empty()) {
+ return R_WOULDBLOCK;
+ }
+
+ while (aCount > 0 && !mReadQueue.empty()) {
+ const NrTcpSocketData& data = mReadQueue.front();
+
+ size_t remainingCount = data.GetData().Length() - mReadOffset;
+ size_t amountToCopy = std::min(aCount, remainingCount);
+
+ char* buffer = static_cast<char*>(aBuffer) + (*aRead);
+
+ memcpy(buffer, data.GetData().Elements() + mReadOffset, amountToCopy);
+
+ mReadOffset += amountToCopy;
+ *aRead += amountToCopy;
+ aCount -= amountToCopy;
+
+ if (remainingCount == amountToCopy) {
+ mReadOffset = 0;
+ mReadQueue.pop_front();
+ }
+ }
+
+ return 0;
+}
+
+int NrTcpSocket::getaddr(nr_transport_addr* aAddr) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::getaddr %p\n", this);
+ return nr_transport_addr_copy(aAddr, &my_addr_);
+}
+
+int NrTcpSocket::sendto(const void* aBuffer, size_t aCount, int aFlags,
+ const nr_transport_addr* aAddr) {
+ // never call this
+ MOZ_ASSERT(0);
+ return R_FAILED;
+}
+
+int NrTcpSocket::recvfrom(void* aBuffer, size_t aCount, size_t* aRead,
+ int aFlags, nr_transport_addr* aAddr) {
+ // never call this
+ MOZ_ASSERT(0);
+ return R_FAILED;
+}
+
+int NrTcpSocket::listen(int aBacklog) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::listen %p\n", this);
+ return R_INTERNAL;
+}
+
+int NrTcpSocket::accept(nr_transport_addr* aAddr, nr_socket** aSocket) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::accept %p\n", this);
+ return R_INTERNAL;
+}
+
+// WebrtcTCPSocketCallback
+void NrTcpSocket::OnClose(nsresult aReason) {
+ nsCString errorName;
+ GetErrorName(aReason, errorName);
+
+ r_log(LOG_GENERIC, LOG_ERR, "NrTcpSocket::OnClose %p reason=%u name=%s\n",
+ this, static_cast<uint32_t>(aReason), errorName.get());
+
+ close();
+
+ DoCallbacks();
+}
+
+void NrTcpSocket::OnConnected(const nsACString& aProxyType) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnConnected %p\n", this);
+ if (aProxyType != "" && aProxyType != "direct") {
+ my_addr_.is_proxied = true;
+ }
+
+ DoCallbacks();
+}
+
+void NrTcpSocket::OnRead(nsTArray<uint8_t>&& aReadData) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "NrTcpSocket::OnRead %p read=%zu\n", this,
+ aReadData.Length());
+
+ mReadQueue.emplace_back(std::move(aReadData));
+
+ DoCallbacks();
+}
+
+void NrTcpSocket::DoCallbacks() {
+ size_t lastCount = -1;
+ size_t currentCount = 0;
+ while ((poll_flags() & PR_POLL_READ) != 0 &&
+ // Make sure whatever is reading knows we're closed. This doesn't need
+ // to happen for writes since ICE doesn't like a failing writable.
+ (mClosed || (currentCount = CountUnreadBytes()) > 0) &&
+ lastCount != currentCount) {
+ fire_callback(NR_ASYNC_WAIT_READ);
+ lastCount = currentCount;
+ }
+
+ // We're always ready to write after we're connected. The parent process will
+ // buffer writes for us.
+ if (!mClosed && mWebrtcTCPSocket && (poll_flags() & PR_POLL_WRITE) != 0) {
+ fire_callback(NR_ASYNC_WAIT_WRITE);
+ }
+}
+
+size_t NrTcpSocket::CountUnreadBytes() const {
+ size_t count = 0;
+
+ for (const NrTcpSocketData& data : mReadQueue) {
+ count += data.GetData().Length();
+ }
+
+ MOZ_ASSERT(count >= mReadOffset, "offset exceeds read buffer length");
+
+ count -= mReadOffset;
+
+ return count;
+}
+
+void NrTcpSocket::AssignChannel_DoNotUse(WebrtcTCPSocketWrapper* aWrapper) {
+ mWebrtcTCPSocket = aWrapper;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/nr_socket_tcp.h b/dom/media/webrtc/transport/nr_socket_tcp.h
new file mode 100644
index 0000000000..ebf806a924
--- /dev/null
+++ b/dom/media/webrtc/transport/nr_socket_tcp.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+/*
+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_tcp_h__
+#define nr_socket_tcp_h__
+
+#include <list>
+
+#include "mozilla/net/WebrtcTCPSocketCallback.h"
+
+#include "nsTArray.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "transport_addr.h"
+}
+
+#include "nr_socket_prsock.h"
+
+namespace mozilla {
+using namespace net;
+
+namespace net {
+class WebrtcTCPSocketWrapper;
+} // namespace net
+
+class NrTcpSocketData;
+class NrSocketProxyConfig;
+
+class NrTcpSocket : public NrSocketBase, public WebrtcTCPSocketCallback {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrTcpSocket, override)
+
+ explicit NrTcpSocket(const std::shared_ptr<NrSocketProxyConfig>& aConfig);
+
+ // NrSocketBase
+ int create(nr_transport_addr* aAddr) override;
+ int connect(const nr_transport_addr* aAddr) override;
+ void close() override;
+ int write(const void* aBuffer, size_t aCount, size_t* aWrote) override;
+ int read(void* aBuffer, size_t aCount, size_t* aRead) override;
+ int getaddr(nr_transport_addr* aAddr) override;
+ int sendto(const void* aBuffer, size_t aCount, int aFlags,
+ const nr_transport_addr* aAddr) override;
+ int recvfrom(void* aBuffer, size_t aCount, size_t* aRead, int aFlags,
+ nr_transport_addr* aAddr) override;
+ int listen(int aBacklog) override;
+ int accept(nr_transport_addr* aAddr, nr_socket** aSocket) override;
+
+ // WebrtcTCPSocketCallback
+ void OnClose(nsresult aReason) override;
+ void OnConnected(const nsACString& aProxyType) override;
+ void OnRead(nsTArray<uint8_t>&& aReadData) override;
+
+ size_t CountUnreadBytes() const;
+
+ // for gtests
+ void AssignChannel_DoNotUse(WebrtcTCPSocketWrapper* aWrapper);
+
+ protected:
+ virtual ~NrTcpSocket();
+
+ private:
+ void DoCallbacks();
+
+ bool mClosed;
+
+ size_t mReadOffset;
+ std::list<NrTcpSocketData> mReadQueue;
+
+ std::shared_ptr<NrSocketProxyConfig> mConfig;
+
+ RefPtr<WebrtcTCPSocketWrapper> mWebrtcTCPSocket;
+};
+
+} // namespace mozilla
+
+#endif // nr_socket_tcp_h__
diff --git a/dom/media/webrtc/transport/nr_timer.cpp b/dom/media/webrtc/transport/nr_timer.cpp
new file mode 100644
index 0000000000..3d5671c7a4
--- /dev/null
+++ b/dom/media/webrtc/transport/nr_timer.cpp
@@ -0,0 +1,256 @@
+/* -*- 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 "nsServiceManagerUtils.h"
+#include "nsIEventTarget.h"
+#include "nsINamed.h"
+#include "nsITimer.h"
+#include "nsNetCID.h"
+#include "runnable_utils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/UniquePtr.h"
+
+extern "C" {
+#include "async_wait.h"
+#include "async_timer.h"
+#include "r_errors.h"
+#include "r_log.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() = default;
+
+ 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 nsINamed {
+ 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.
+ }
+
+ NS_IMETHOD
+ GetName(nsACString& aName) override {
+ aName.AssignLiteral("nrappkitTimerCallback");
+ return NS_OK;
+ }
+
+ private:
+ nsCOMPtr<nsITimer> timer_;
+ virtual ~nrappkitTimerCallback() = default;
+};
+
+NS_IMPL_ISUPPORTS(nrappkitTimerCallback, nsITimerCallback, nsINamed)
+
+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_(nullptr, 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_(nullptr, 0, cb_arg_);
+ }
+ }
+
+ virtual void Cancel() override { cb_ = nullptr; }
+
+ ~nrappkitScheduledCallback() = default;
+};
+
+} // namespace mozilla
+
+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(UniquePtr<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();
+
+ nrappkitTimerCallback* callback = new nrappkitTimerCallback(cb, arg, func, l);
+
+ nsCOMPtr<nsITimer> timer;
+ rv = NS_NewTimerWithCallback(getter_AddRefs(timer), 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/dom/media/webrtc/transport/nricectx.cpp b/dom/media/webrtc/transport/nricectx.cpp
new file mode 100644
index 0000000000..697eecdb07
--- /dev/null
+++ b/dom/media/webrtc/transport/nricectx.cpp
@@ -0,0 +1,1106 @@
+/* -*- 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 "nr_socket_proxy_config.h"
+#include "nsXULAppAPI.h"
+
+#include "logging.h"
+#include "pk11pub.h"
+#include "plbase64.h"
+
+#include "nsCOMPtr.h"
+#include "nsError.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "ScopedNSSTypes.h"
+#include "runnable_utils.h"
+#include "nsIUUIDGenerator.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 "transport_addr.h"
+#include "nr_crypto.h"
+#include "nr_socket.h"
+#include "nr_socket_local.h"
+#include "stun_reg.h"
+#include "stun_util.h"
+#include "ice_codeword.h"
+#include "ice_ctx.h"
+#include "ice_candidate.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 {
+
+using std::shared_ptr;
+
+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";
+const char kNrIceTransportTls[] = "tls";
+
+static bool initialized = false;
+
+static int noop(void** obj) { return 0; }
+
+static nr_socket_factory_vtbl ctx_socket_factory_vtbl = {nr_socket_local_create,
+ noop};
+
+// Implement NSPR-based crypto algorithms
+static int nr_crypto_nss_random_bytes(UCHAR* buf, size_t len) {
+ UniquePK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) return R_INTERNAL;
+
+ SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), buf, len);
+ if (rv != SECSuccess) return R_INTERNAL;
+
+ return 0;
+}
+
+static int nr_crypto_nss_hmac(UCHAR* key, size_t keyl, UCHAR* buf, size_t bufl,
+ UCHAR* result) {
+ CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
+ PK11SlotInfo* slot = nullptr;
+ MOZ_ASSERT(keyl > 0);
+ SECItem keyi = {siBuffer, key, static_cast<unsigned int>(keyl)};
+ PK11SymKey* skey = nullptr;
+ PK11Context* hmac_ctx = nullptr;
+ 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, size_t 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));
+ uint8_t protocol;
+ if (transport_ == kNrIceTransportUdp) {
+ protocol = IPPROTO_UDP;
+ } else if (transport_ == kNrIceTransportTcp) {
+ protocol = IPPROTO_TCP;
+ } else if (transport_ == kNrIceTransportTls) {
+ protocol = IPPROTO_TCP;
+ } else {
+ MOZ_MTLOG(ML_ERROR, "Unsupported STUN server transport: " << transport_);
+ return NS_ERROR_FAILURE;
+ }
+
+ if (has_addr_) {
+ if (transport_ == kNrIceTransportTls) {
+ // Refuse to try TLS without an FQDN
+ return NS_ERROR_INVALID_ARG;
+ }
+ r = nr_praddr_to_transport_addr(&addr_, &server->addr, protocol, 0);
+ if (r) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ MOZ_ASSERT(sizeof(server->addr.fqdn) > host_.size());
+ // Dummy information to keep nICEr happy
+ if (use_ipv6_if_fqdn_) {
+ nr_str_port_to_transport_addr("::", port_, protocol, &server->addr);
+ } else {
+ nr_str_port_to_transport_addr("0.0.0.0", port_, protocol, &server->addr);
+ }
+ PL_strncpyz(server->addr.fqdn, host_.c_str(), sizeof(server->addr.fqdn));
+ if (transport_ == kNrIceTransportTls) {
+ server->addr.tls = 1;
+ }
+ }
+
+ nr_transport_addr_fmt_addr_string(&server->addr);
+
+ 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.
+ const UCHAR* data = password_.empty() ? nullptr : &password_[0];
+ int r = r_data_create(&server->password, data, password_.size());
+ if (r) {
+ RFREE(server->username);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+NrIceCtx::NrIceCtx(const std::string& name)
+ : connection_state_(ICE_CTX_INIT),
+ gathering_state_(ICE_CTX_GATHER_INIT),
+ name_(name),
+ ice_controlling_set_(false),
+ streams_(),
+ ctx_(nullptr),
+ peer_(nullptr),
+ ice_handler_vtbl_(nullptr),
+ ice_handler_(nullptr),
+ trickle_(true),
+ config_(),
+ nat_(nullptr),
+ proxy_config_(nullptr) {}
+
+/* static */
+RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& aName) {
+ RefPtr<NrIceCtx> ctx = new NrIceCtx(aName);
+
+ if (!ctx->Initialize()) {
+ return nullptr;
+ }
+
+ return ctx;
+}
+
+nsresult NrIceCtx::SetIceConfig(const Config& aConfig) {
+ config_ = aConfig;
+ switch (config_.mPolicy) {
+ case ICE_POLICY_RELAY:
+ MOZ_MTLOG(ML_DEBUG, "SetIceConfig: relay only");
+ nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES);
+ nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_RELAY_ONLY);
+ break;
+ case ICE_POLICY_NO_HOST:
+ MOZ_MTLOG(ML_DEBUG, "SetIceConfig: no host");
+ nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES);
+ nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_RELAY_ONLY);
+ break;
+ case ICE_POLICY_ALL:
+ MOZ_MTLOG(ML_DEBUG, "SetIceConfig: all");
+ nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES);
+ nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_RELAY_ONLY);
+ break;
+ }
+
+ // TODO: Support re-configuring the test NAT someday?
+ if (!nat_ && config_.mNatSimulatorConfig.isSome()) {
+ TestNat* test_nat = new TestNat;
+ test_nat->filtering_type_ = TestNat::ToNatBehavior(
+ config_.mNatSimulatorConfig->mFilteringType.get());
+ test_nat->mapping_type_ =
+ TestNat::ToNatBehavior(config_.mNatSimulatorConfig->mMappingType.get());
+ test_nat->block_udp_ = config_.mNatSimulatorConfig->mBlockUdp;
+ test_nat->block_tcp_ = config_.mNatSimulatorConfig->mBlockTcp;
+ test_nat->block_tls_ = config_.mNatSimulatorConfig->mBlockTls;
+ test_nat->error_code_for_drop_ =
+ config_.mNatSimulatorConfig->mErrorCodeForDrop;
+ if (config_.mNatSimulatorConfig->mRedirectAddress.Length()) {
+ test_nat
+ ->stun_redirect_map_[config_.mNatSimulatorConfig->mRedirectAddress] =
+ config_.mNatSimulatorConfig->mRedirectTargets;
+ }
+ test_nat->enabled_ = true;
+ SetNat(test_nat);
+ }
+
+ return NS_OK;
+}
+
+RefPtr<NrIceMediaStream> NrIceCtx::CreateStream(const std::string& id,
+ const std::string& name,
+ int components) {
+ if (streams_.count(id)) {
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ RefPtr<NrIceMediaStream> stream =
+ new NrIceMediaStream(this, id, name, components);
+ streams_[id] = stream;
+ return stream;
+}
+
+void NrIceCtx::DestroyStream(const std::string& id) {
+ auto it = streams_.find(id);
+ if (it != streams_.end()) {
+ auto preexisting_stream = it->second;
+ streams_.erase(it);
+ preexisting_stream->Close();
+ }
+
+ if (streams_.empty()) {
+ SetGatheringState(ICE_CTX_GATHER_INIT);
+ }
+}
+
+// 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);
+ MOZ_ASSERT(stream->local_stream);
+ MOZ_ASSERT(!stream->local_stream->obsolete);
+
+ return 0;
+}
+
+int NrIceCtx::stream_ready(void* obj, nr_ice_media_stream* stream) {
+ MOZ_MTLOG(ML_DEBUG, "stream_ready called");
+ MOZ_ASSERT(!stream->local_stream);
+ MOZ_ASSERT(!stream->obsolete);
+
+ // 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");
+ MOZ_ASSERT(!stream->local_stream);
+ MOZ_ASSERT(!stream->obsolete);
+
+ // 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->Failed();
+ 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) {
+ if (stream->obsolete) {
+ // Stream was probably just marked obsolete, resulting in this callback
+ return;
+ }
+ // 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;
+ }
+
+ if (!candidate) {
+ s->SignalCandidate(s, "", stream->ufrag, "", "");
+ return;
+ }
+
+ std::string actual_addr;
+ std::string mdns_addr;
+ ctx->GenerateObfuscatedAddress(candidate, &mdns_addr, &actual_addr);
+
+ // Format the candidate.
+ char candidate_str[NR_ICE_MAX_ATTRIBUTE_SIZE];
+ int r = nr_ice_format_candidate_attribute(
+ candidate, candidate_str, sizeof(candidate_str),
+ (ctx->ctx()->flags & NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES) ? 1 : 0);
+ MOZ_ASSERT(!r);
+ if (r) return;
+
+ MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate "
+ << candidate_str);
+
+ s->SignalCandidate(s, candidate_str, stream->ufrag, mdns_addr, actual_addr);
+}
+
+void NrIceCtx::InitializeGlobals(const GlobalConfig& aConfig) {
+ RLogConnector::CreateInstance();
+ // 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);
+ NR_reg_set_uint4((char*)"stun.client.maximum_transmits",
+ aConfig.mStunClientMaxTransmits);
+ NR_reg_set_uint4((char*)NR_ICE_REG_TRICKLE_GRACE_PERIOD,
+ aConfig.mTrickleIceGracePeriod);
+ NR_reg_set_int4((char*)NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT,
+ aConfig.mIceTcpSoSockCount);
+ NR_reg_set_int4((char*)NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG,
+ aConfig.mIceTcpListenBacklog);
+
+ NR_reg_set_char((char*)NR_ICE_REG_ICE_TCP_DISABLE, !aConfig.mTcpEnabled);
+
+ if (aConfig.mAllowLoopback) {
+ NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, 1);
+ }
+
+ if (aConfig.mAllowLinkLocal) {
+ NR_reg_set_char((char*)NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, 1);
+ }
+ if (!aConfig.mForceNetInterface.Length()) {
+ NR_reg_set_string((char*)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME,
+ const_cast<char*>(aConfig.mForceNetInterface.get()));
+ }
+
+ // For now, always use nr_resolver for UDP.
+ NR_reg_set_char((char*)NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP, 1);
+
+ // Use nr_resolver for TCP only when not in e10s mode (for unit-tests)
+ if (XRE_IsParentProcess()) {
+ NR_reg_set_char((char*)NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP, 1);
+ }
+ }
+}
+
+void NrIceCtx::SetTargetForDefaultLocalAddressLookup(
+ const std::string& target_ip, uint16_t target_port) {
+ nr_ice_set_target_for_default_local_address_lookup(ctx_, target_ip.c_str(),
+ target_port);
+}
+
+#define MAXADDRS 100 // mirrors setting in ice_ctx.c
+
+/* static */
+nsTArray<NrIceStunAddr> NrIceCtx::GetStunAddrs() {
+ nsTArray<NrIceStunAddr> addrs;
+
+ nr_local_addr local_addrs[MAXADDRS];
+ int addr_ct = 0;
+
+ // most likely running on parent process and need crypto vtbl
+ // initialized on Windows (Linux and OSX don't seem to care)
+ if (!initialized) {
+ nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
+ }
+
+ MOZ_MTLOG(ML_INFO, "NrIceCtx static call to find local stun addresses");
+ if (nr_stun_find_local_addresses(local_addrs, MAXADDRS, &addr_ct)) {
+ MOZ_MTLOG(ML_INFO, "Error finding local stun addresses");
+ } else {
+ for (int i = 0; i < addr_ct; ++i) {
+ NrIceStunAddr addr(&local_addrs[i]);
+ addrs.AppendElement(addr);
+ }
+ }
+
+ return addrs;
+}
+
+void NrIceCtx::SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs) {
+ nr_local_addr* local_addrs;
+ local_addrs = new nr_local_addr[addrs.Length()];
+
+ for (size_t i = 0; i < addrs.Length(); ++i) {
+ nr_local_addr_copy(&local_addrs[i],
+ const_cast<nr_local_addr*>(&addrs[i].localAddr()));
+ }
+ nr_ice_set_local_addresses(ctx_, local_addrs, addrs.Length());
+
+ delete[] local_addrs;
+}
+
+bool NrIceCtx::Initialize() {
+ // Create the ICE context
+ int r;
+
+ UINT4 flags = NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
+ r = nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, &ctx_);
+
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'");
+ return false;
+ }
+
+ // override default factory to capture optional proxy config when creating
+ // sockets.
+ nr_socket_factory* factory;
+ r = nr_socket_factory_create_int(this, &ctx_socket_factory_vtbl, &factory);
+
+ if (r) {
+ MOZ_MTLOG(LogLevel::Error, "Couldn't create ctx socket factory.");
+ return false;
+ }
+ nr_ice_ctx_set_socket_factory(ctx_, factory);
+
+ 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;
+ }
+ }
+
+ // 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;
+ }
+
+ nsresult rv;
+ 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;
+}
+
+void NrIceCtx::AccumulateStats(const NrIceStats& stats) {
+ nr_accumulate_count(&(ctx_->stats.stun_retransmits), stats.stun_retransmits);
+ nr_accumulate_count(&(ctx_->stats.turn_401s), stats.turn_401s);
+ nr_accumulate_count(&(ctx_->stats.turn_403s), stats.turn_403s);
+ nr_accumulate_count(&(ctx_->stats.turn_438s), stats.turn_438s);
+}
+
+NrIceStats NrIceCtx::Destroy() {
+ // designed to be called more than once so if stats are desired, this can be
+ // called just prior to the destructor
+ MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__);
+
+ for (auto& idAndStream : streams_) {
+ idAndStream.second->Close();
+ }
+
+ NrIceStats stats;
+ if (ctx_) {
+ stats.stun_retransmits = ctx_->stats.stun_retransmits;
+ stats.turn_401s = ctx_->stats.turn_401s;
+ stats.turn_403s = ctx_->stats.turn_403s;
+ stats.turn_438s = ctx_->stats.turn_438s;
+ }
+
+ if (peer_) {
+ nr_ice_peer_ctx_destroy(&peer_);
+ }
+ if (ctx_) {
+ nr_ice_ctx_destroy(&ctx_);
+ }
+
+ delete ice_handler_vtbl_;
+ delete ice_handler_;
+
+ ice_handler_vtbl_ = nullptr;
+ ice_handler_ = nullptr;
+ proxy_config_ = nullptr;
+ streams_.clear();
+
+ return stats;
+}
+
+NrIceCtx::~NrIceCtx() = default;
+
+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::SetStunServers(
+ const std::vector<NrIceStunServer>& stun_servers) {
+ MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__);
+ // We assume nr_ice_stun_server is memmoveable. That's true right now.
+ std::vector<nr_ice_stun_server> servers;
+
+ for (size_t i = 0; i < stun_servers.size(); ++i) {
+ nr_ice_stun_server server;
+ nsresult rv = stun_servers[i].ToNicerStunStruct(&server);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't convert STUN server for '" << name_ << "'");
+ } else {
+ servers.push_back(server);
+ }
+ }
+
+ int r = nr_ice_ctx_set_stun_servers(ctx_, servers.data(),
+ static_cast<int>(servers.size()));
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set STUN servers 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) {
+ MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__);
+ // We assume nr_ice_turn_server is memmoveable. That's true right now.
+ std::vector<nr_ice_turn_server> servers;
+
+ for (size_t i = 0; i < turn_servers.size(); ++i) {
+ nr_ice_turn_server server;
+ nsresult rv = turn_servers[i].ToNicerTurnStruct(&server);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't convert TURN server for '" << name_ << "'");
+ } else {
+ servers.push_back(server);
+ }
+ }
+
+ int r = nr_ice_ctx_set_turn_servers(ctx_, servers.data(),
+ static_cast<int>(servers.size()));
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set TURN servers for '" << name_ << "'");
+ // TODO(ekr@rtfm.com): This leaks the username/password. Need to free that.
+ return NS_ERROR_FAILURE;
+ }
+
+ 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::SetProxyConfig(NrSocketProxyConfig&& config) {
+ proxy_config_.reset(new NrSocketProxyConfig(std::move(config)));
+ if (nat_) {
+ nat_->set_proxy_config(proxy_config_);
+ }
+
+ if (proxy_config_->GetForceProxy()) {
+ 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);
+ }
+
+ return NS_OK;
+}
+
+void NrIceCtx::SetCtxFlags(bool default_route_only) {
+ ASSERT_ON_THREAD(sts_target_);
+
+ 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);
+ }
+}
+
+nsresult NrIceCtx::StartGathering(bool default_route_only,
+ bool obfuscate_host_addresses) {
+ ASSERT_ON_THREAD(sts_target_);
+ MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__);
+
+ if (obfuscate_host_addresses) {
+ nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES);
+ }
+
+ SetCtxFlags(default_route_only);
+
+ // 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) {
+ SetGatheringState(ICE_CTX_GATHER_STARTED);
+ } else {
+ SetGatheringState(ICE_CTX_GATHER_COMPLETE);
+ MOZ_MTLOG(ML_ERROR, "ICE FAILED: 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 (auto& idAndStream : streams_) {
+ if (idAndStream.second->HasStream(stream)) {
+ return idAndStream.second;
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector<std::string> NrIceCtx::GetGlobalAttributes() {
+ char** attrs = nullptr;
+ 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;
+ attrs_in.reserve(attrs.size());
+ for (auto& attr : attrs) {
+ attrs_in.push_back(const_cast<char*>(attr.c_str()));
+ }
+
+ int r = nr_ice_peer_ctx_parse_global_attributes(
+ peer_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size());
+ if (r) {
+ MOZ_MTLOG(ML_ERROR,
+ "Couldn't parse global attributes for " << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+bool NrIceCtx::HasStreamsToConnect() const {
+ for (auto& idAndStream : streams_) {
+ if (idAndStream.second->state() != NrIceMediaStream::ICE_CLOSED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+nsresult NrIceCtx::StartChecks() {
+ int r;
+ MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): " << __func__);
+
+ if (!HasStreamsToConnect()) {
+ MOZ_MTLOG(ML_NOTICE, "In StartChecks, nothing to do on " << name_);
+ return NS_OK;
+ }
+
+ r = nr_ice_peer_ctx_pair_candidates(peer_);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "ICE FAILED: 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_INFO, "Couldn't start peer checks on "
+ << name_ << ", assuming trickle ICE");
+ } else {
+ MOZ_MTLOG(ML_ERROR,
+ "ICE FAILED: 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);
+}
+
+void NrIceCtx::UpdateNetworkState(bool online) {
+ MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << name_ << "): updating network state to "
+ << (online ? "online" : "offline"));
+ if (connection_state_ == ICE_CTX_CLOSED) {
+ return;
+ }
+
+ if (online) {
+ nr_ice_peer_ctx_refresh_consent_all_streams(peer_);
+ } else {
+ nr_ice_peer_ctx_disconnect_all_streams(peer_);
+ }
+}
+
+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& log : logs) {
+ MOZ_MTLOG(ML_INFO, log);
+ }
+ }
+
+ 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);
+}
+
+void NrIceCtx::GenerateObfuscatedAddress(nr_ice_candidate* candidate,
+ std::string* mdns_address,
+ std::string* actual_address) {
+ if (candidate->type == HOST &&
+ (ctx_->flags & NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES)) {
+ char addr[64];
+ if (nr_transport_addr_get_addrstring(&candidate->addr, addr,
+ sizeof(addr))) {
+ return;
+ }
+
+ *actual_address = addr;
+
+ const auto& iter = obfuscated_host_addresses_.find(*actual_address);
+ if (iter != obfuscated_host_addresses_.end()) {
+ *mdns_address = iter->second;
+ } else {
+ nsresult rv;
+ nsCOMPtr<nsIUUIDGenerator> uuidgen =
+ do_GetService("@mozilla.org/uuid-generator;1", &rv);
+ // If this fails, we'll return a zero UUID rather than something
+ // unexpected.
+ nsID id = {};
+ id.Clear();
+ if (NS_SUCCEEDED(rv)) {
+ rv = uuidgen->GenerateUUIDInPlace(&id);
+ if (NS_FAILED(rv)) {
+ id.Clear();
+ }
+ }
+
+ char chars[NSID_LENGTH];
+ id.ToProvidedString(chars);
+ // The string will look like {64888863-a253-424a-9b30-1ed285d20142},
+ // we want to trim off the braces.
+ const char* ptr_to_id = chars;
+ ++ptr_to_id;
+ chars[NSID_LENGTH - 2] = 0;
+
+ std::ostringstream o;
+ o << ptr_to_id << ".local";
+ *mdns_address = o.str();
+
+ obfuscated_host_addresses_[*actual_address] = *mdns_address;
+ }
+ candidate->mdns_addr = r_strdup(mdns_address->c_str());
+ }
+}
+
+} // namespace mozilla
+
+// 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;
+}
+
+int nr_socket_local_create(void* obj, nr_transport_addr* addr,
+ nr_socket** sockp) {
+ using namespace mozilla;
+
+ RefPtr<NrSocketBase> sock;
+ int r, _status;
+ shared_ptr<NrSocketProxyConfig> config = nullptr;
+
+ if (obj) {
+ config = static_cast<NrIceCtx*>(obj)->GetProxyConfig();
+ }
+
+ r = NrSocketBase::CreateSocket(addr, &sock, config);
+ 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;
+}
diff --git a/dom/media/webrtc/transport/nricectx.h b/dom/media/webrtc/transport/nricectx.h
new file mode 100644
index 0000000000..a0a0b5b772
--- /dev/null
+++ b/dom/media/webrtc/transport/nricectx.h
@@ -0,0 +1,421 @@
+/* -*- 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 <memory>
+#include <string>
+#include <vector>
+#include <map>
+
+#include "sigslot.h"
+
+#include "prnetdb.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIEventTarget.h"
+#include "nsTArray.h"
+#include "mozilla/Maybe.h"
+
+#include "m_cpp_utils.h"
+#include "nricestunaddr.h"
+#include "nricemediastream.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 {
+
+class NrSocketProxyConfig;
+
+class NrIceMediaStream;
+
+extern const char kNrIceTransportUdp[];
+extern const char kNrIceTransportTcp[];
+extern const char kNrIceTransportTls[];
+
+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;
+
+ bool HasFqdn() const { return !has_addr_; }
+
+ void SetUseIPv6IfFqdn() {
+ MOZ_ASSERT(HasFqdn());
+ use_ipv6_if_fqdn_ = true;
+ }
+
+ 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_;
+ bool use_ipv6_if_fqdn_ = false;
+};
+
+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 TestNat;
+
+class NrIceStats {
+ public:
+ uint16_t stun_retransmits = 0;
+ uint16_t turn_401s = 0;
+ uint16_t turn_403s = 0;
+ uint16_t turn_438s = 0;
+};
+
+class NrIceCtx {
+ 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 };
+
+ struct NatSimulatorConfig {
+ bool mBlockTcp = false;
+ bool mBlockUdp = false;
+ bool mBlockTls = false;
+ int mErrorCodeForDrop = 0;
+ nsCString mMappingType = "ENDPOINT_INDEPENDENT"_ns;
+ nsCString mFilteringType = "ENDPOINT_INDEPENDENT"_ns;
+ nsCString mRedirectAddress;
+ CopyableTArray<nsCString> mRedirectTargets;
+ };
+
+ struct Config {
+ NrIceCtx::Policy mPolicy = NrIceCtx::ICE_POLICY_ALL;
+ Maybe<NatSimulatorConfig> mNatSimulatorConfig;
+ };
+
+ static RefPtr<NrIceCtx> Create(const std::string& aName);
+
+ nsresult SetIceConfig(const Config& aConfig);
+
+ RefPtr<NrIceMediaStream> CreateStream(const std::string& id,
+ const std::string& name,
+ int components);
+ void DestroyStream(const std::string& id);
+
+ struct GlobalConfig {
+ bool mAllowLinkLocal = false;
+ bool mAllowLoopback = false;
+ bool mTcpEnabled = true;
+ int mStunClientMaxTransmits = 7;
+ int mTrickleIceGracePeriod = 5000;
+ int mIceTcpSoSockCount = 3;
+ int mIceTcpListenBacklog = 10;
+ nsCString mForceNetInterface;
+ };
+
+ // initialize ICE globals, crypto, and logging
+ static void InitializeGlobals(const GlobalConfig& aConfig);
+
+ void SetTargetForDefaultLocalAddressLookup(const std::string& target_ip,
+ uint16_t target_port);
+
+ // static GetStunAddrs for use in parent process to support
+ // sandboxing restrictions
+ static nsTArray<NrIceStunAddr> GetStunAddrs();
+ void SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs);
+
+ bool Initialize();
+
+ 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();
+
+ RefPtr<NrIceMediaStream> GetStream(const std::string& id) {
+ auto it = streams_.find(id);
+ if (it != streams_.end()) {
+ return it->second;
+ }
+ return nullptr;
+ }
+
+ std::vector<RefPtr<NrIceMediaStream>> GetStreams() const {
+ std::vector<RefPtr<NrIceMediaStream>> result;
+ for (auto& idAndStream : streams_) {
+ result.push_back(idAndStream.second);
+ }
+ return result;
+ }
+
+ bool HasStreamsToConnect() const;
+
+ // The name of the ctx
+ const std::string& name() const { return name_; }
+
+ // 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 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 SetProxyConfig(NrSocketProxyConfig&& config);
+
+ const std::shared_ptr<NrSocketProxyConfig>& GetProxyConfig() {
+ return proxy_config_;
+ }
+
+ void SetCtxFlags(bool default_route_only);
+
+ // Start ICE gathering
+ nsresult StartGathering(bool default_route_only,
+ bool obfuscate_host_addresses);
+
+ // Start checking
+ nsresult StartChecks();
+
+ // Notify that the network has gone online/offline
+ void UpdateNetworkState(bool online);
+
+ void AccumulateStats(const NrIceStats& stats);
+ NrIceStats Destroy();
+
+ // 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:
+ explicit NrIceCtx(const std::string& name);
+
+ 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);
+
+ void GenerateObfuscatedAddress(nr_ice_candidate* candidate,
+ std::string* mdns_address,
+ std::string* actual_address);
+
+ ConnectionState connection_state_;
+ GatheringState gathering_state_;
+ const std::string name_;
+ bool ice_controlling_set_;
+ std::map<std::string, 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
+ Config config_;
+ RefPtr<TestNat> nat_;
+ std::shared_ptr<NrSocketProxyConfig> proxy_config_;
+ std::map<std::string, std::string> obfuscated_host_addresses_;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/nricemediastream.cpp b/dom/media/webrtc/transport/nricemediastream.cpp
new file mode 100644
index 0000000000..ef7980dcf3
--- /dev/null
+++ b/dom/media/webrtc/transport/nricemediastream.cpp
@@ -0,0 +1,709 @@
+/* -*- 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"
+#include "nsThreadUtils.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "transport_addr.h"
+#include "nr_socket.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:
+ if (addr.tls) {
+ out->transport = kNrIceTransportTls;
+ } else {
+ 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->mdns_addr) {
+ out->mdns_addr = cand->mdns_addr;
+ }
+
+ if (cand->isock) {
+ nr_transport_addr addr;
+ r = nr_socket_getaddr(cand->isock->sock, &addr);
+ if (r) return false;
+
+ out->is_proxied = addr.is_proxied;
+
+ 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;
+ out->label = candc.label;
+ out->trickled = candc.trickled;
+ out->priority = candc.priority;
+ 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;
+}
+
+static bool Matches(const nr_ice_media_stream* stream, const std::string& ufrag,
+ const std::string& pwd) {
+ return stream && (stream->ufrag == ufrag) && (stream->pwd == pwd);
+}
+
+NrIceMediaStream::NrIceMediaStream(NrIceCtx* ctx, const std::string& id,
+ const std::string& name, size_t components)
+ : state_(ICE_CONNECTING),
+ ctx_(ctx),
+ name_(name),
+ components_(components),
+ stream_(nullptr),
+ old_stream_(nullptr),
+ id_(id) {}
+
+NrIceMediaStream::~NrIceMediaStream() {
+ // We do not need to destroy anything. All major resources
+ // are attached to the ice ctx.
+}
+
+nsresult NrIceMediaStream::ConnectToPeer(
+ const std::string& ufrag, const std::string& pwd,
+ const std::vector<std::string>& attributes) {
+ MOZ_ASSERT(stream_);
+
+ if (Matches(old_stream_, ufrag, pwd)) {
+ // (We swap before we close so we never have stream_ == nullptr)
+ MOZ_MTLOG(ML_DEBUG,
+ "Rolling back to old stream ufrag=" << ufrag << " " << name_);
+ std::swap(stream_, old_stream_);
+ CloseStream(&old_stream_);
+ } else if (old_stream_) {
+ // Right now we wait for ICE to complete before closing the old stream.
+ // It might be worth it to close it sooner, but we don't want to close it
+ // right away.
+ MOZ_MTLOG(ML_DEBUG,
+ "ICE restart committed, marking old stream as obsolete, "
+ "beginning switchover to ufrag="
+ << ufrag << " " << name_);
+ nr_ice_media_stream_set_obsolete(old_stream_);
+ }
+
+ nr_ice_media_stream* peer_stream;
+ if (nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream)) {
+ // No peer yet
+ std::vector<char*> attributes_in;
+ attributes_in.reserve(attributes.size());
+ for (auto& attribute : attributes) {
+ MOZ_MTLOG(ML_DEBUG, "Setting " << attribute << " on stream " << name_);
+ attributes_in.push_back(const_cast<char*>(attribute.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.empty() ? nullptr : &attributes_in[0],
+ attributes_in.size());
+ if (r) {
+ MOZ_MTLOG(ML_ERROR,
+ "Couldn't parse attributes for stream " << name_ << "'");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult NrIceMediaStream::SetIceCredentials(const std::string& ufrag,
+ const std::string& pwd) {
+ if (Matches(stream_, ufrag, pwd)) {
+ return NS_OK;
+ }
+
+ if (Matches(old_stream_, ufrag, pwd)) {
+ return NS_OK;
+ }
+
+ MOZ_MTLOG(ML_DEBUG, "Setting ICE credentials for " << name_ << " - " << ufrag
+ << ":" << pwd);
+ CloseStream(&old_stream_);
+ old_stream_ = stream_;
+
+ std::string name(name_ + " - " + ufrag + ":" + pwd);
+
+ int r = nr_ice_add_media_stream(ctx_->ctx(), name.c_str(), ufrag.c_str(),
+ pwd.c_str(), components_, &stream_);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create ICE media stream for '"
+ << name_ << "': error=" << r);
+ stream_ = old_stream_;
+ old_stream_ = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ state_ = ICE_CONNECTING;
+ return NS_OK;
+}
+
+// Parse trickle ICE candidate
+nsresult NrIceMediaStream::ParseTrickleCandidate(const std::string& candidate,
+ const std::string& ufrag,
+ const std::string& mdns_addr) {
+ nr_ice_media_stream* stream = GetStreamForRemoteUfrag(ufrag);
+ if (!stream) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_MTLOG(ML_NOTICE, "NrIceCtx(" << ctx_->ctx()->label << ")/STREAM("
+ << name() << ") : parsing trickle candidate "
+ << candidate);
+
+ int r = nr_ice_peer_ctx_parse_trickle_candidate(
+ ctx_->peer(), stream, const_cast<char*>(candidate.c_str()),
+ mdns_addr.c_str());
+
+ if (r) {
+ if (r == R_ALREADY) {
+ MOZ_MTLOG(ML_INFO, "Trickle candidate is redundant for stream '"
+ << name_
+ << "' because it is completed: " << candidate);
+ } else if (r == R_REJECTED) {
+ MOZ_MTLOG(ML_INFO,
+ "Trickle candidate is ignored for stream '"
+ << name_
+ << "', probably because it is for an unused component"
+ << ": " << candidate);
+ } else {
+ MOZ_MTLOG(ML_ERROR, "Couldn't parse trickle candidate for stream '"
+ << name_ << "': " << candidate);
+ 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 = std::move(local);
+ if (remotep) *remotep = std::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.component_id = p1->remote->component->component_id;
+
+ // As discussed with drno: a component's can_send field (set to true
+ // by ICE consent) is a very close approximation for writable and
+ // readable. Note: the component for the local candidate never has
+ // the can_send member set to true, remote for both readable and
+ // writable. (mjf)
+ pair.writable = p1->remote->component->can_send;
+ pair.readable = p1->remote->component->can_send;
+ pair.selected =
+ p1->remote->component && p1->remote->component->active == p1;
+ pair.codeword = p1->codeword;
+ pair.bytes_sent = p1->bytes_sent;
+ pair.bytes_recvd = p1->bytes_recvd;
+ pair.ms_since_last_send =
+ p1->last_sent.tv_sec * 1000 + p1->last_sent.tv_usec / 1000;
+ pair.ms_since_last_recv =
+ p1->last_recvd.tv_sec * 1000 + p1->last_recvd.tv_usec / 1000;
+
+ 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 {
+ if (!stream_) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nr_ice_candidate* cand;
+
+ int r = nr_ice_media_stream_get_default_candidate(stream_, component, &cand);
+ if (r) {
+ if (r == R_NOT_FOUND) {
+ MOZ_MTLOG(ML_INFO, "Couldn't get default ICE candidate for '"
+ << name_ << "', no candidates.");
+ } else {
+ MOZ_MTLOG(ML_ERROR, "Couldn't get default ICE candidate for '"
+ << name_ << "', " << r);
+ }
+ 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::GetAttributes() const {
+ char** attrs = nullptr;
+ 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;
+}
+
+bool NrIceMediaStream::HasStream(nr_ice_media_stream* stream) const {
+ return (stream == stream_) || (stream == old_stream_);
+}
+
+nsresult NrIceMediaStream::SendPacket(int component_id,
+ const unsigned char* data, size_t len) {
+ nr_ice_media_stream* stream = old_stream_ ? old_stream_ : stream_;
+ 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;
+ NS_DispatchToCurrentThread(NewRunnableMethod<nr_ice_media_stream*>(
+ "NrIceMediaStream::DeferredCloseOldStream", this,
+ &NrIceMediaStream::DeferredCloseOldStream, old_stream_));
+ SignalReady(this);
+ } else {
+ MOZ_MTLOG(ML_DEBUG,
+ "Stream ready callback fired again for '" << name_ << "'");
+ }
+}
+
+void NrIceMediaStream::Failed() {
+ if (state_ != ICE_CLOSED) {
+ MOZ_MTLOG(ML_DEBUG, "Marking stream failed '" << name_ << "'");
+ state_ = ICE_CLOSED;
+ // We don't need the old stream anymore.
+ NS_DispatchToCurrentThread(NewRunnableMethod<nr_ice_media_stream*>(
+ "NrIceMediaStream::DeferredCloseOldStream", this,
+ &NrIceMediaStream::DeferredCloseOldStream, old_stream_));
+ SignalFailed(this);
+ }
+}
+
+void NrIceMediaStream::Close() {
+ MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
+ state_ = ICE_CLOSED;
+
+ CloseStream(&old_stream_);
+ CloseStream(&stream_);
+ ctx_ = nullptr;
+}
+
+void NrIceMediaStream::CloseStream(nr_ice_media_stream** stream) {
+ if (*stream) {
+ int r = nr_ice_remove_media_stream(ctx_->ctx(), stream);
+ if (r) {
+ MOZ_ASSERT(false, "Failed to remove stream");
+ MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r);
+ }
+ *stream = nullptr;
+ }
+}
+
+void NrIceMediaStream::DeferredCloseOldStream(const nr_ice_media_stream* old) {
+ if (old == old_stream_) {
+ CloseStream(&old_stream_);
+ }
+}
+
+nr_ice_media_stream* NrIceMediaStream::GetStreamForRemoteUfrag(
+ const std::string& aUfrag) {
+ if (aUfrag.empty()) {
+ return stream_;
+ }
+
+ nr_ice_media_stream* peer_stream = nullptr;
+
+ if (!nr_ice_peer_ctx_find_pstream(ctx_->peer(), stream_, &peer_stream) &&
+ aUfrag == peer_stream->ufrag) {
+ return stream_;
+ }
+
+ if (old_stream_ &&
+ !nr_ice_peer_ctx_find_pstream(ctx_->peer(), old_stream_, &peer_stream) &&
+ aUfrag == peer_stream->ufrag) {
+ return old_stream_;
+ }
+
+ return nullptr;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/nricemediastream.h b/dom/media/webrtc/transport/nricemediastream.h
new file mode 100644
index 0000000000..f18b09c47f
--- /dev/null
+++ b/dom/media/webrtc/transport/nricemediastream.h
@@ -0,0 +1,225 @@
+/* -*- 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 "nscore.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;
+ std::string mdns_addr;
+ Type type;
+ TcpType tcp_type;
+ std::string codeword;
+ std::string label;
+ bool trickled;
+ uint32_t priority;
+ bool is_proxied = false;
+};
+
+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;
+ bool writable;
+ bool readable;
+ // 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;
+ uint64_t component_id;
+
+ // for RTCIceCandidatePairStats
+ uint64_t bytes_sent;
+ uint64_t bytes_recvd;
+ uint64_t ms_since_last_send;
+ uint64_t ms_since_last_recv;
+};
+
+class NrIceMediaStream {
+ public:
+ NrIceMediaStream(NrIceCtx* ctx, const std::string& id,
+ const std::string& name, size_t components);
+
+ nsresult SetIceCredentials(const std::string& ufrag, const std::string& pwd);
+ nsresult ConnectToPeer(const std::string& ufrag, const std::string& pwd,
+ const std::vector<std::string>& peer_attrs);
+ 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 ICE attributes; used for testing
+ std::vector<std::string> GetAttributes() 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 trickle ICE candidate
+ nsresult ParseTrickleCandidate(const std::string& candidate,
+ const std::string& ufrag,
+ const std::string& mdns_addr);
+
+ // 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_; }
+
+ bool HasStream(nr_ice_media_stream* stream) const;
+ // 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();
+ void Failed();
+
+ // 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 transport
+ // the candidate belongs to.
+ const std::string& GetId() const { return id_; }
+
+ sigslot::signal5<NrIceMediaStream*, const std::string&, const std::string&,
+ const std::string&, 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();
+
+ DISALLOW_COPY_ASSIGN(NrIceMediaStream);
+
+ void CloseStream(nr_ice_media_stream** stream);
+ void DeferredCloseOldStream(const nr_ice_media_stream* old);
+ nr_ice_media_stream* GetStreamForRemoteUfrag(const std::string& ufrag);
+
+ State state_;
+ RefPtr<NrIceCtx> ctx_;
+ const std::string name_;
+ const size_t components_;
+ nr_ice_media_stream* stream_;
+ nr_ice_media_stream* old_stream_;
+ const std::string id_;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/nriceresolver.cpp b/dom/media/webrtc/transport/nriceresolver.cpp
new file mode 100644
index 0000000000..e1737e0e65
--- /dev/null
+++ b/dom/media/webrtc/transport/nriceresolver.cpp
@@ -0,0 +1,234 @@
+/* -*- 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 "transport/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_WARN_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 = nullptr;
+ 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;
+ nsIDNSService::DNSFlags resolve_flags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
+ OriginAttributes attrs;
+
+ 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_->AsyncResolveNative(
+ nsAutoCString(resource->domain_name),
+ nsIDNSService::RESOLVE_TYPE_DEFAULT, resolve_flags, nullptr, pr,
+ sts_thread_, attrs, 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* aRecord, 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;
+ nsCOMPtr<nsIDNSAddrRecord> record = do_QueryInterface(aRecord);
+ if (record && 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/dom/media/webrtc/transport/nriceresolver.h b/dom/media/webrtc/transport/nriceresolver.h
new file mode 100644
index 0000000000..7399c78050
--- /dev/null
+++ b/dom/media/webrtc/transport/nriceresolver.h
@@ -0,0 +1,119 @@
+/* -*- 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 "nsIDNSService.h"
+#include "nsIDNSListener.h"
+#include "nsICancelable.h"
+#include "nricectx.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() = default;
+
+ 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/dom/media/webrtc/transport/nriceresolverfake.cpp b/dom/media/webrtc/transport/nriceresolverfake.cpp
new file mode 100644
index 0000000000..93ef37efc1
--- /dev/null
+++ b/dom/media/webrtc/transport/nriceresolverfake.cpp
@@ -0,0 +1,174 @@
+/* -*- 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 "prio.h"
+#include "mozilla/Assertions.h"
+
+extern "C" {
+#include "async_wait.h"
+#include "async_timer.h"
+#include "nr_resolver.h"
+#include "r_macros.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 = nullptr;
+
+ 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/dom/media/webrtc/transport/nriceresolverfake.h b/dom/media/webrtc/transport/nriceresolverfake.h
new file mode 100644
index 0000000000..e46ce603de
--- /dev/null
+++ b/dom/media/webrtc/transport/nriceresolverfake.h
@@ -0,0 +1,137 @@
+/* -*- 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 "csi_platform.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/dom/media/webrtc/transport/nricestunaddr.cpp b/dom/media/webrtc/transport/nricestunaddr.cpp
new file mode 100644
index 0000000000..d1e8d3ac52
--- /dev/null
+++ b/dom/media/webrtc/transport/nricestunaddr.cpp
@@ -0,0 +1,93 @@
+/* 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 "logging.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "local_addr.h"
+}
+
+// Local includes
+#include "nricestunaddr.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+NrIceStunAddr::NrIceStunAddr() : localAddr_(new nr_local_addr) {
+ memset(localAddr_, 0, sizeof(nr_local_addr));
+}
+
+NrIceStunAddr::NrIceStunAddr(const nr_local_addr* addr)
+ : localAddr_(new nr_local_addr) {
+ nr_local_addr_copy(localAddr_, const_cast<nr_local_addr*>(addr));
+}
+
+NrIceStunAddr::NrIceStunAddr(const NrIceStunAddr& rhs)
+ : localAddr_(new nr_local_addr) {
+ nr_local_addr_copy(localAddr_, const_cast<nr_local_addr*>(rhs.localAddr_));
+}
+
+NrIceStunAddr::~NrIceStunAddr() { delete localAddr_; }
+
+size_t NrIceStunAddr::SerializationBufferSize() const {
+ return sizeof(nr_local_addr);
+}
+
+nsresult NrIceStunAddr::Serialize(char* buffer, size_t buffer_size) const {
+ if (buffer_size != sizeof(nr_local_addr)) {
+ MOZ_MTLOG(ML_ERROR,
+ "Failed trying to serialize NrIceStunAddr, "
+ "input buffer length ("
+ << buffer_size << ") does not match required length ("
+ << sizeof(nr_local_addr) << ")");
+ MOZ_ASSERT(false, "Failed to serialize NrIceStunAddr, bad buffer size");
+ return NS_ERROR_FAILURE;
+ }
+
+ nr_local_addr* toAddr = (nr_local_addr*)buffer;
+ if (nr_local_addr_copy(toAddr, localAddr_)) {
+ MOZ_MTLOG(ML_ERROR,
+ "Failed trying to serialize NrIceStunAddr, "
+ "could not copy nr_local_addr.");
+ MOZ_ASSERT(false,
+ "Failed to serialize NrIceStunAddr, nr_local_addr_copy failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult NrIceStunAddr::Deserialize(const char* buffer, size_t buffer_size) {
+ if (buffer_size != sizeof(nr_local_addr)) {
+ MOZ_MTLOG(ML_ERROR,
+ "Failed trying to deserialize NrIceStunAddr, "
+ "input buffer length ("
+ << buffer_size << ") does not match required length ("
+ << sizeof(nr_local_addr) << ")");
+ MOZ_ASSERT(false, "Failed to deserialize NrIceStunAddr, bad buffer size");
+ return NS_ERROR_FAILURE;
+ }
+
+ nr_local_addr* from_addr =
+ const_cast<nr_local_addr*>((const nr_local_addr*)buffer);
+
+ // At this point, from_addr->addr.addr is invalid (null), but will
+ // be fixed by nr_local_addr_copy.
+ if (nr_local_addr_copy(localAddr_, from_addr)) {
+ MOZ_MTLOG(ML_ERROR,
+ "Failed trying to deserialize NrIceStunAddr, "
+ "could not copy nr_local_addr.");
+ MOZ_ASSERT(
+ false,
+ "Failed to deserialize NrIceStunAddr, nr_local_addr_copy failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/nricestunaddr.h b/dom/media/webrtc/transport/nricestunaddr.h
new file mode 100644
index 0000000000..8ebcdd6fe7
--- /dev/null
+++ b/dom/media/webrtc/transport/nricestunaddr.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 nricestunaddr_h__
+#define nricestunaddr_h__
+
+#include "nsError.h" // for nsresult
+
+typedef struct nr_local_addr_ nr_local_addr;
+
+namespace mozilla {
+
+class NrIceStunAddr {
+ public:
+ NrIceStunAddr(); // needed for IPC deserialization
+ explicit NrIceStunAddr(const nr_local_addr* addr);
+ NrIceStunAddr(const NrIceStunAddr& rhs);
+
+ ~NrIceStunAddr();
+
+ const nr_local_addr& localAddr() const { return *localAddr_; }
+
+ // serialization/deserialization helper functions for use
+ // in dom/media/webrtc/transport/ipc/NrIceStunAddrMessagUtils.h
+ size_t SerializationBufferSize() const;
+ nsresult Serialize(char* buffer, size_t buffer_size) const;
+ nsresult Deserialize(const char* buffer, size_t buffer_size);
+
+ private:
+ nr_local_addr* localAddr_;
+};
+
+} // namespace mozilla
+
+#endif // nricestunaddr_h__
diff --git a/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp b/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp
new file mode 100644
index 0000000000..f022f8c29a
--- /dev/null
+++ b/dom/media/webrtc/transport/nrinterfaceprioritizer.cpp
@@ -0,0 +1,258 @@
+/* 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 "nr_api.h"
+#include "nrinterfaceprioritizer.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 + 47];
+ 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;
+ }
+ if (type & NR_INTERFACE_TYPE_TEREDO) {
+ // Teredo gets penalty because it's IP relayed
+ return 5;
+ }
+ 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 (const auto& local_addr : local_addrs_) {
+ if (tmp_pref == 0) {
+ return R_FAILED;
+ }
+ preference_map_.insert(make_pair(local_addr.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 = nullptr;
+ 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/dom/media/webrtc/transport/nrinterfaceprioritizer.h b/dom/media/webrtc/transport/nrinterfaceprioritizer.h
new file mode 100644
index 0000000000..051b586445
--- /dev/null
+++ b/dom/media/webrtc/transport/nrinterfaceprioritizer.h
@@ -0,0 +1,17 @@
+/* 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_interface_prioritizer.h"
+}
+
+namespace mozilla {
+
+nr_interface_prioritizer* CreateInterfacePrioritizer();
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/transport/rlogconnector.cpp b/dom/media/webrtc/transport/rlogconnector.cpp
new file mode 100644
index 0000000000..bdb58aac56
--- /dev/null
+++ b/dom/media/webrtc/transport/rlogconnector.cpp
@@ -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: bcampen@mozilla.com */
+
+#include "rlogconnector.h"
+
+#include <cstdarg>
+#include <deque>
+#include <string>
+#include <utility> // Pinch hitting for <utility> and std::move
+#include <vector>
+
+#include "logging.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Sprintf.h"
+
+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) {
+ if (mozilla::RLogConnector::GetInstance()->ShouldLog(level)) {
+ // 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() = default;
+
+void RLogConnector::SetLogLimit(uint32_t new_limit) {
+ OffTheBooksMutexAutoLock lock(mutex_);
+ log_limit_ = new_limit;
+ RemoveOld();
+}
+
+bool RLogConnector::ShouldLog(int level) const {
+ return level <= LOG_INFO ||
+ MOZ_LOG_TEST(getLogModule(), rLogLvlToMozLogLvl(level));
+}
+
+void RLogConnector::Log(int level, std::string&& log) {
+ MOZ_MTLOG(rLogLvlToMozLogLvl(level), log);
+ OffTheBooksMutexAutoLock lock(mutex_);
+ if (disableCount_ == 0) {
+ AddMsg(std::move(log));
+ }
+}
+
+void RLogConnector::AddMsg(std::string&& msg) {
+ log_messages_.push_front(std::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/dom/media/webrtc/transport/rlogconnector.h b/dom/media/webrtc/transport/rlogconnector.h
new file mode 100644
index 0000000000..8236eb2ab3
--- /dev/null
+++ b/dom/media/webrtc/transport/rlogconnector.h
@@ -0,0 +1,127 @@
+/* -*- 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);
+ bool ShouldLog(int level) const;
+ 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/dom/media/webrtc/transport/runnable_utils.h b/dom/media/webrtc/transport/runnable_utils.h
new file mode 100644
index 0000000000..79f23dac2d
--- /dev/null
+++ b/dom/media/webrtc/transport/runnable_utils.h
@@ -0,0 +1,221 @@
+/* -*- 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 <utility>
+
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+#include <functional>
+#include <tuple>
+#include <type_traits>
+
+// 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) {
+ return thread->Dispatch(runnable, flags);
+}
+
+template <RunnableResult result>
+class runnable_args_base : public Runnable {
+ public:
+ runnable_args_base() : Runnable("media-runnable_args_base") {}
+
+ NS_IMETHOD Run() final {
+ MOZ_ASSERT(!mHasRun, "Can only be run once");
+
+ RunInternal();
+#ifdef DEBUG
+ mHasRun = true;
+#endif
+
+ return NS_OK;
+ }
+
+ protected:
+ virtual void RunInternal() = 0;
+#ifdef DEBUG
+ bool mHasRun = false;
+#endif
+};
+
+} // namespace detail
+
+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|.
+ template <typename... Arguments>
+ explicit runnable_args_func(FunType f, Arguments&&... args)
+ : mFunc(f), mArgs(std::forward<Arguments>(args)...) {}
+
+ protected:
+ void RunInternal() override {
+ std::apply(std::move(mFunc), std::move(mArgs));
+ }
+
+ private:
+ FunType mFunc;
+ std::tuple<Args...> mArgs;
+};
+
+template <typename FunType, typename... Args>
+runnable_args_func<FunType, std::decay_t<Args>...>* WrapRunnableNM(
+ FunType f, Args&&... args) {
+ return new runnable_args_func<FunType, std::decay_t<Args>...>(
+ f, std::forward<Args>(args)...);
+}
+
+template <typename Ret, typename FunType, typename... Args>
+class runnable_args_func_ret
+ : public detail::runnable_args_base<detail::ReturnsResult> {
+ public:
+ template <typename... Arguments>
+ runnable_args_func_ret(Ret* ret, FunType f, Arguments&&... args)
+ : mReturn(ret), mFunc(f), mArgs(std::forward<Arguments>(args)...) {}
+
+ protected:
+ void RunInternal() override {
+ *mReturn = std::apply(std::move(mFunc), std::move(mArgs));
+ }
+
+ private:
+ Ret* mReturn;
+ FunType mFunc;
+ std::tuple<Args...> mArgs;
+};
+
+template <typename R, typename FunType, typename... Args>
+runnable_args_func_ret<R, FunType, std::decay_t<Args>...>* WrapRunnableNMRet(
+ R* ret, FunType f, Args&&... args) {
+ return new runnable_args_func_ret<R, FunType, std::decay_t<Args>...>(
+ ret, f, std::forward<Args>(args)...);
+}
+
+template <typename Class, typename M, typename... Args>
+class runnable_args_memfn
+ : public detail::runnable_args_base<detail::NoResult> {
+ public:
+ template <typename... Arguments>
+ runnable_args_memfn(Class&& obj, M method, Arguments&&... args)
+ : mObj(std::forward<Class>(obj)),
+ mMethod(method),
+ mArgs(std::forward<Arguments>(args)...) {}
+
+ protected:
+ void RunInternal() override {
+ std::apply(std::mem_fn(mMethod),
+ std::tuple_cat(std::tie(mObj), std::move(mArgs)));
+ }
+
+ private:
+ // For holders such as RefPtr and UniquePtr make sure concrete copy is held
+ // rather than a potential dangling reference.
+ std::decay_t<Class> mObj;
+ M mMethod;
+ std::tuple<Args...> mArgs;
+};
+
+template <typename Class, typename M, typename... Args>
+runnable_args_memfn<Class, M, std::decay_t<Args>...>* WrapRunnable(
+ Class&& obj, M method, Args&&... args) {
+ return new runnable_args_memfn<Class, M, std::decay_t<Args>...>(
+ std::forward<Class>(obj), method, std::forward<Args>(args)...);
+}
+
+template <typename Ret, typename Class, typename M, typename... Args>
+class runnable_args_memfn_ret
+ : public detail::runnable_args_base<detail::ReturnsResult> {
+ public:
+ template <typename... Arguments>
+ runnable_args_memfn_ret(Ret* ret, Class&& obj, M method, Arguments... args)
+ : mReturn(ret),
+ mObj(std::forward<Class>(obj)),
+ mMethod(method),
+ mArgs(std::forward<Arguments>(args)...) {}
+
+ protected:
+ void RunInternal() override {
+ *mReturn = std::apply(std::mem_fn(mMethod),
+ std::tuple_cat(std::tie(mObj), std::move(mArgs)));
+ }
+
+ private:
+ Ret* mReturn;
+ // For holders such as RefPtr and UniquePtr make sure concrete copy is held
+ // rather than a potential dangling reference.
+ std::decay_t<Class> mObj;
+ M mMethod;
+ std::tuple<Args...> mArgs;
+};
+
+template <typename R, typename Class, typename M, typename... Args>
+runnable_args_memfn_ret<R, Class, M, std::decay_t<Args>...>* WrapRunnableRet(
+ R* ret, Class&& obj, M method, Args&&... args) {
+ return new runnable_args_memfn_ret<R, Class, M, std::decay_t<Args>...>(
+ ret, std::forward<Class>(obj), method, std::forward<Args>(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) {}
+
+ protected:
+ void RunInternal() override { ref_ = nullptr; }
+
+ 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/dom/media/webrtc/transport/sigslot.h b/dom/media/webrtc/transport/sigslot.h
new file mode 100644
index 0000000000..448e8137fe
--- /dev/null
+++ b/dom/media/webrtc/transport/sigslot.h
@@ -0,0 +1,619 @@
+// 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 WEBRTC_WIN 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 RTC_BASE_SIGSLOT_H_
+#define RTC_BASE_SIGSLOT_H_
+
+#include <stdlib.h>
+#include <cstring>
+#include <list>
+#include <set>
+
+// On our copy of sigslot.h, we set single threading as default.
+#define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+
+#if defined(SIGSLOT_PURE_ISO) || \
+ (!defined(WEBRTC_WIN) && !defined(__GNUG__) && \
+ !defined(SIGSLOT_USE_POSIX_THREADS))
+# define _SIGSLOT_SINGLE_THREADED
+#elif defined(WEBRTC_WIN)
+# define _SIGSLOT_HAS_WIN32_THREADS
+# if !defined(WIN32_LEAN_AND_MEAN)
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include "rtc_base/win32.h"
+#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 rtc?
+namespace sigslot {
+
+class single_threaded {
+ public:
+ void lock() {}
+ void unlock() {}
+};
+
+#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;
+ }
+ }
+
+ void lock() { EnterCriticalSection(get_critsec()); }
+
+ 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);
+ }
+
+ ~multi_threaded_local() { DeleteCriticalSection(&m_critsec); }
+
+ void lock() { EnterCriticalSection(&m_critsec); }
+
+ 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:
+ void lock() { pthread_mutex_lock(get_mutex()); }
+ void unlock() { pthread_mutex_unlock(get_mutex()); }
+
+ private:
+ static pthread_mutex_t* get_mutex();
+};
+
+class multi_threaded_local {
+ public:
+ multi_threaded_local() { pthread_mutex_init(&m_mutex, nullptr); }
+ multi_threaded_local(const multi_threaded_local&) {
+ pthread_mutex_init(&m_mutex, nullptr);
+ }
+ ~multi_threaded_local() { pthread_mutex_destroy(&m_mutex); }
+ void lock() { pthread_mutex_lock(&m_mutex); }
+ void unlock() { pthread_mutex_unlock(&m_mutex); }
+
+ private:
+ pthread_mutex_t m_mutex;
+};
+#endif // _SIGSLOT_HAS_POSIX_THREADS
+
+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 _signal_base_interface;
+
+class has_slots_interface {
+ private:
+ typedef void (*signal_connect_t)(has_slots_interface* self,
+ _signal_base_interface* sender);
+ typedef void (*signal_disconnect_t)(has_slots_interface* self,
+ _signal_base_interface* sender);
+ typedef void (*disconnect_all_t)(has_slots_interface* self);
+
+ const signal_connect_t m_signal_connect;
+ const signal_disconnect_t m_signal_disconnect;
+ const disconnect_all_t m_disconnect_all;
+
+ protected:
+ has_slots_interface(signal_connect_t conn, signal_disconnect_t disc,
+ disconnect_all_t disc_all)
+ : m_signal_connect(conn),
+ m_signal_disconnect(disc),
+ m_disconnect_all(disc_all) {}
+
+ // Doesn't really need to be virtual, but is for backwards compatibility
+ // (it was virtual in a previous version of sigslot).
+ virtual ~has_slots_interface() = default;
+
+ public:
+ void signal_connect(_signal_base_interface* sender) {
+ m_signal_connect(this, sender);
+ }
+
+ void signal_disconnect(_signal_base_interface* sender) {
+ m_signal_disconnect(this, sender);
+ }
+
+ void disconnect_all() { m_disconnect_all(this); }
+};
+
+class _signal_base_interface {
+ private:
+ typedef void (*slot_disconnect_t)(_signal_base_interface* self,
+ has_slots_interface* pslot);
+ typedef void (*slot_duplicate_t)(_signal_base_interface* self,
+ const has_slots_interface* poldslot,
+ has_slots_interface* pnewslot);
+
+ const slot_disconnect_t m_slot_disconnect;
+ const slot_duplicate_t m_slot_duplicate;
+
+ protected:
+ _signal_base_interface(slot_disconnect_t disc, slot_duplicate_t dupl)
+ : m_slot_disconnect(disc), m_slot_duplicate(dupl) {}
+
+ ~_signal_base_interface() = default;
+
+ public:
+ void slot_disconnect(has_slots_interface* pslot) {
+ m_slot_disconnect(this, pslot);
+ }
+
+ void slot_duplicate(const has_slots_interface* poldslot,
+ has_slots_interface* pnewslot) {
+ m_slot_duplicate(this, poldslot, pnewslot);
+ }
+};
+
+class _opaque_connection {
+ private:
+ typedef void (*emit_t)(const _opaque_connection*);
+ template <typename FromT, typename ToT>
+ union union_caster {
+ FromT from;
+ ToT to;
+ };
+
+ emit_t pemit;
+ has_slots_interface* pdest;
+ // Pointers to member functions may be up to 16 bytes for virtual classes,
+ // so make sure we have enough space to store it.
+ unsigned char pmethod[16];
+
+ public:
+ template <typename DestT, typename... Args>
+ _opaque_connection(DestT* pd, void (DestT::*pm)(Args...)) : pdest(pd) {
+ typedef void (DestT::*pm_t)(Args...);
+ static_assert(sizeof(pm_t) <= sizeof(pmethod),
+ "Size of slot function pointer too large.");
+
+ std::memcpy(pmethod, &pm, sizeof(pm_t));
+
+ typedef void (*em_t)(const _opaque_connection* self, Args...);
+ union_caster<em_t, emit_t> caster2;
+ caster2.from = &_opaque_connection::emitter<DestT, Args...>;
+ pemit = caster2.to;
+ }
+
+ has_slots_interface* getdest() const { return pdest; }
+
+ _opaque_connection duplicate(has_slots_interface* newtarget) const {
+ _opaque_connection res = *this;
+ res.pdest = newtarget;
+ return res;
+ }
+
+ // Just calls the stored "emitter" function pointer stored at construction
+ // time.
+ template <typename... Args>
+ void emit(Args... args) const {
+ typedef void (*em_t)(const _opaque_connection*, Args...);
+ union_caster<emit_t, em_t> caster;
+ caster.from = pemit;
+ (caster.to)(this, args...);
+ }
+
+ private:
+ template <typename DestT, typename... Args>
+ static void emitter(const _opaque_connection* self, Args... args) {
+ typedef void (DestT::*pm_t)(Args...);
+ pm_t pm;
+ std::memcpy(&pm, self->pmethod, sizeof(pm_t));
+ (static_cast<DestT*>(self->pdest)->*(pm))(args...);
+ }
+};
+
+template <class mt_policy>
+class _signal_base : public _signal_base_interface, public mt_policy {
+ protected:
+ typedef std::list<_opaque_connection> connections_list;
+
+ _signal_base()
+ : _signal_base_interface(&_signal_base::do_slot_disconnect,
+ &_signal_base::do_slot_duplicate),
+ m_current_iterator(m_connected_slots.end()) {}
+
+ ~_signal_base() { disconnect_all(); }
+
+ private:
+ _signal_base& operator=(_signal_base const& that);
+
+ public:
+ _signal_base(const _signal_base& o)
+ : _signal_base_interface(&_signal_base::do_slot_disconnect,
+ &_signal_base::do_slot_duplicate),
+ m_current_iterator(m_connected_slots.end()) {
+ lock_block<mt_policy> lock(this);
+ for (const auto& connection : o.m_connected_slots) {
+ connection.getdest()->signal_connect(this);
+ m_connected_slots.push_back(connection);
+ }
+ }
+
+ bool is_empty() {
+ lock_block<mt_policy> lock(this);
+ return m_connected_slots.empty();
+ }
+
+ void disconnect_all() {
+ lock_block<mt_policy> lock(this);
+
+ while (!m_connected_slots.empty()) {
+ has_slots_interface* pdest = m_connected_slots.front().getdest();
+ m_connected_slots.pop_front();
+ pdest->signal_disconnect(static_cast<_signal_base_interface*>(this));
+ }
+ // If disconnect_all is called while the signal is firing, advance the
+ // current slot iterator to the end to avoid an invalidated iterator from
+ // being dereferenced.
+ m_current_iterator = m_connected_slots.end();
+ }
+
+#if !defined(NDEBUG)
+ bool connected(has_slots_interface* pclass) {
+ lock_block<mt_policy> lock(this);
+ connections_list::const_iterator it = m_connected_slots.begin();
+ connections_list::const_iterator itEnd = m_connected_slots.end();
+ while (it != itEnd) {
+ if (it->getdest() == pclass) return true;
+ ++it;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots_interface* pclass) {
+ lock_block<mt_policy> lock(this);
+ connections_list::iterator it = m_connected_slots.begin();
+ connections_list::iterator itEnd = m_connected_slots.end();
+
+ while (it != itEnd) {
+ if (it->getdest() == pclass) {
+ // If we're currently using this iterator because the signal is firing,
+ // advance it to avoid it being invalidated.
+ if (m_current_iterator == it) {
+ m_current_iterator = m_connected_slots.erase(it);
+ } else {
+ m_connected_slots.erase(it);
+ }
+ pclass->signal_disconnect(static_cast<_signal_base_interface*>(this));
+ return;
+ }
+ ++it;
+ }
+ }
+
+ private:
+ static void do_slot_disconnect(_signal_base_interface* p,
+ has_slots_interface* pslot) {
+ _signal_base* const self = static_cast<_signal_base*>(p);
+ lock_block<mt_policy> lock(self);
+ connections_list::iterator it = self->m_connected_slots.begin();
+ connections_list::iterator itEnd = self->m_connected_slots.end();
+
+ while (it != itEnd) {
+ connections_list::iterator itNext = it;
+ ++itNext;
+
+ if (it->getdest() == pslot) {
+ // If we're currently using this iterator because the signal is firing,
+ // advance it to avoid it being invalidated.
+ if (self->m_current_iterator == it) {
+ self->m_current_iterator = self->m_connected_slots.erase(it);
+ } else {
+ self->m_connected_slots.erase(it);
+ }
+ }
+
+ it = itNext;
+ }
+ }
+
+ static void do_slot_duplicate(_signal_base_interface* p,
+ const has_slots_interface* oldtarget,
+ has_slots_interface* newtarget) {
+ _signal_base* const self = static_cast<_signal_base*>(p);
+ lock_block<mt_policy> lock(self);
+ connections_list::iterator it = self->m_connected_slots.begin();
+ connections_list::iterator itEnd = self->m_connected_slots.end();
+
+ while (it != itEnd) {
+ if (it->getdest() == oldtarget) {
+ self->m_connected_slots.push_back(it->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+
+ // Used to handle a slot being disconnected while a signal is
+ // firing (iterating m_connected_slots).
+ connections_list::iterator m_current_iterator;
+ bool m_erase_current_iterator = false;
+};
+
+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_interface(&has_slots::do_signal_connect,
+ &has_slots::do_signal_disconnect,
+ &has_slots::do_disconnect_all) {}
+
+ has_slots(has_slots const& o)
+ : has_slots_interface(&has_slots::do_signal_connect,
+ &has_slots::do_signal_disconnect,
+ &has_slots::do_disconnect_all) {
+ lock_block<mt_policy> lock(this);
+ for (auto* sender : o.m_senders) {
+ sender->slot_duplicate(&o, this);
+ m_senders.insert(sender);
+ }
+ }
+
+ ~has_slots() { this->disconnect_all(); }
+
+ private:
+ has_slots& operator=(has_slots const&);
+
+ static void do_signal_connect(has_slots_interface* p,
+ _signal_base_interface* sender) {
+ has_slots* const self = static_cast<has_slots*>(p);
+ lock_block<mt_policy> lock(self);
+ self->m_senders.insert(sender);
+ }
+
+ static void do_signal_disconnect(has_slots_interface* p,
+ _signal_base_interface* sender) {
+ has_slots* const self = static_cast<has_slots*>(p);
+ lock_block<mt_policy> lock(self);
+ self->m_senders.erase(sender);
+ }
+
+ static void do_disconnect_all(has_slots_interface* p) {
+ has_slots* const self = static_cast<has_slots*>(p);
+ lock_block<mt_policy> lock(self);
+ while (!self->m_senders.empty()) {
+ std::set<_signal_base_interface*> senders;
+ senders.swap(self->m_senders);
+ const_iterator it = senders.begin();
+ const_iterator itEnd = senders.end();
+
+ while (it != itEnd) {
+ _signal_base_interface* s = *it;
+ ++it;
+ s->slot_disconnect(p);
+ }
+ }
+ }
+
+ private:
+ sender_set m_senders;
+};
+
+template <class mt_policy, typename... Args>
+class signal_with_thread_policy : public _signal_base<mt_policy> {
+ private:
+ typedef _signal_base<mt_policy> base;
+
+ protected:
+ typedef typename base::connections_list connections_list;
+
+ public:
+ signal_with_thread_policy() = default;
+
+ template <class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(Args...)) {
+ lock_block<mt_policy> lock(this);
+ this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun));
+ pclass->signal_connect(static_cast<_signal_base_interface*>(this));
+ }
+
+ void emit(Args... args) {
+ lock_block<mt_policy> lock(this);
+ this->m_current_iterator = this->m_connected_slots.begin();
+ while (this->m_current_iterator != this->m_connected_slots.end()) {
+ _opaque_connection const& conn = *this->m_current_iterator;
+ ++(this->m_current_iterator);
+ conn.emit<Args...>(args...);
+ }
+ }
+
+ void operator()(Args... args) { emit(args...); }
+};
+
+// Alias with default thread policy. Needed because both default arguments
+// and variadic template arguments must go at the end of the list, so we
+// can't have both at once.
+template <typename... Args>
+using signal = signal_with_thread_policy<SIGSLOT_DEFAULT_MT_POLICY, Args...>;
+
+// The previous verion of sigslot didn't use variadic templates, so you would
+// need to write "sigslot::signal2<Arg1, Arg2>", for example.
+// Now you can just write "sigslot::signal<Arg1, Arg2>", but these aliases
+// exist for backwards compatibility.
+template <typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal0 = signal_with_thread_policy<mt_policy>;
+
+template <typename A1, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal1 = signal_with_thread_policy<mt_policy, A1>;
+
+template <typename A1, typename A2,
+ typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal2 = signal_with_thread_policy<mt_policy, A1, A2>;
+
+template <typename A1, typename A2, typename A3,
+ typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal3 = signal_with_thread_policy<mt_policy, A1, A2, A3>;
+
+template <typename A1, typename A2, typename A3, typename A4,
+ typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal4 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4>;
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal5 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5>;
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal6 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6>;
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7,
+ typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal7 =
+ signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7>;
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7, typename A8,
+ typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+using signal8 =
+ signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7, A8>;
+
+} // namespace sigslot
+
+#endif // RTC_BASE_SIGSLOT_H_
diff --git a/dom/media/webrtc/transport/simpletokenbucket.cpp b/dom/media/webrtc/transport/simpletokenbucket.cpp
new file mode 100644
index 0000000000..0509697c7b
--- /dev/null
+++ b/dom/media/webrtc/transport/simpletokenbucket.cpp
@@ -0,0 +1,60 @@
+/* -*- 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/dom/media/webrtc/transport/simpletokenbucket.h b/dom/media/webrtc/transport/simpletokenbucket.h
new file mode 100644
index 0000000000..7e809535b1
--- /dev/null
+++ b/dom/media/webrtc/transport/simpletokenbucket.h
@@ -0,0 +1,54 @@
+/* -*- 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/dom/media/webrtc/transport/srtp/README_MOZILLA b/dom/media/webrtc/transport/srtp/README_MOZILLA
new file mode 100644
index 0000000000..8533b67c53
--- /dev/null
+++ b/dom/media/webrtc/transport/srtp/README_MOZILLA
@@ -0,0 +1,7 @@
+This directory contains build files for libsrtp. The actual library
+source is in $TOPSRCDIR/third_party/libsrtp/
+
+The upstream git repository is https://github.com/cisco/libsrtp
+
+TBD add code and instructions how to do a clean update import without manual
+intervention.
diff --git a/dom/media/webrtc/transport/srtp/moz.build b/dom/media/webrtc/transport/srtp/moz.build
new file mode 100644
index 0000000000..d3d4971a6d
--- /dev/null
+++ b/dom/media/webrtc/transport/srtp/moz.build
@@ -0,0 +1,8 @@
+# -*- 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["MOZ_SRTP"]:
+ DIRS += ["/third_party/libsrtp/src"]
diff --git a/dom/media/webrtc/transport/stun_socket_filter.cpp b/dom/media/webrtc/transport/stun_socket_filter.cpp
new file mode 100644
index 0000000000..b568f97a40
--- /dev/null
+++ b/dom/media/webrtc/transport/stun_socket_filter.cpp
@@ -0,0 +1,432 @@
+/* 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>
+#include <iomanip>
+
+extern "C" {
+#include "nr_api.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "logging.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/net/DNS.h"
+#include "stun_socket_filter.h"
+#include "nr_socket_prsock.h"
+
+namespace {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+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_;
+};
+
+static uint16_t GetPortInfallible(const mozilla::net::NetAddr& aAddr) {
+ uint16_t result = 0;
+ (void)aAddr.GetPort(&result);
+ return result;
+}
+
+static std::ostream& operator<<(std::ostream& aStream, UINT12 aId) {
+ for (int octet : aId.octet) {
+ aStream << std::hex << std::setfill('0') << std::setw(2) << octet;
+ }
+ return aStream;
+}
+
+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() = default;
+
+ 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()) {
+ MOZ_MTLOG(ML_DEBUG, __func__ << this << " Address in whitelist: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr));
+ return true;
+ }
+
+ // 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)) {
+ const nr_stun_message_header* msg =
+ reinterpret_cast<const nr_stun_message_header*>(data);
+ 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);
+ MOZ_MTLOG(ML_DEBUG, __func__ << this
+ << " Allowing known STUN response, "
+ "remembering address in whitelist: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr)
+ << " id=" << msg->id);
+ return true;
+ }
+ }
+ // If it's an incoming STUN request we let it pass and add it to the list of
+ // pending response for white listing once we answer.
+ 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);
+ response_allowed_.insert(PendingSTUNRequest(*remote_addr, msg->id));
+ MOZ_MTLOG(
+ ML_DEBUG,
+ __func__ << this
+ << " Allowing STUN request, will allow packets in return: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr) << " id=" << msg->id);
+ return true;
+ }
+ // Lastly if we have send a STUN request to the destination of this
+ // packet we allow it to send us anything back in case it's for example a
+ // DTLS message (but we don't white list).
+ std::set<PendingSTUNRequest>::iterator it =
+ pending_requests_.find(PendingSTUNRequest(*remote_addr));
+ if (it != pending_requests_.end()) {
+ MOZ_MTLOG(
+ ML_DEBUG,
+ __func__
+ << this
+ << " Allowing packet from source while waiting for a response: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr));
+ return true;
+ }
+
+ MOZ_MTLOG(
+ ML_DEBUG,
+ __func__
+ << " Disallowing packet that is neither a STUN request or response: "
+ << remote_addr->ToString() << ":" << GetPortInfallible(*remote_addr));
+ 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()) {
+ MOZ_MTLOG(ML_DEBUG, __func__ << this << " Address in whitelist: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr));
+ 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));
+ MOZ_MTLOG(
+ ML_DEBUG,
+ __func__ << this
+ << " Allowing STUN request, will allow packets in return: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr) << " id=" << 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()) {
+ white_list_.insert(*remote_addr);
+ response_allowed_.erase(it);
+ MOZ_MTLOG(ML_DEBUG, __func__ << this
+ << " Allowing known STUN response, "
+ "remembering address in whitelist: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr)
+ << " id=" << msg->id);
+ return true;
+ }
+
+ MOZ_MTLOG(ML_DEBUG,
+ __func__ << this << " Disallowing unknown STUN response: "
+ << remote_addr->ToString() << ":"
+ << GetPortInfallible(*remote_addr) << " id=" << msg->id);
+ return false;
+ }
+
+ MOZ_MTLOG(
+ ML_DEBUG,
+ __func__
+ << " Disallowing packet that is neither a STUN request or response: "
+ << remote_addr->ToString() << ":" << GetPortInfallible(*remote_addr));
+ 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() = default;
+
+ 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/dom/media/webrtc/transport/stun_socket_filter.h b/dom/media/webrtc/transport/stun_socket_filter.h
new file mode 100644
index 0000000000..393257577d
--- /dev/null
+++ b/dom/media/webrtc/transport/stun_socket_filter.h
@@ -0,0 +1,41 @@
+/* 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() = default;
+};
+
+#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() = default;
+};
+
+#endif // stun_socket_filter_h__
diff --git a/dom/media/webrtc/transport/test/TestSyncRunnable.cpp b/dom/media/webrtc/transport/test/TestSyncRunnable.cpp
new file mode 100644
index 0000000000..ca671b4e79
--- /dev/null
+++ b/dom/media/webrtc/transport/test/TestSyncRunnable.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 "nsIThread.h"
+#include "nsThreadUtils.h"
+#include "mozilla/SyncRunnable.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+nsIThread* gThread = nullptr;
+
+class TestRunnable : public Runnable {
+ public:
+ TestRunnable() : Runnable("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/dom/media/webrtc/transport/test/buffered_stun_socket_unittest.cpp b/dom/media/webrtc/transport/test/buffered_stun_socket_unittest.cpp
new file mode 100644
index 0000000000..e6a9cd38a2
--- /dev/null
+++ b/dom/media/webrtc/transport/test/buffered_stun_socket_unittest.cpp
@@ -0,0 +1,245 @@
+/* -*- 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
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "nr_socket_buffered_stun.h"
+#include "transport_addr.h"
+}
+
+#include "stun_msg.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() override {
+ 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_ = std::move(dummy); // 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/dom/media/webrtc/transport/test/dummysocket.h b/dom/media/webrtc/transport/test/dummysocket.h
new file mode 100644
index 0000000000..6e20a1f7e7
--- /dev/null
+++ b/dom/media/webrtc/transport/test/dummysocket.h
@@ -0,0 +1,217 @@
+/* -*- 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 "mediapacket.h"
+#include "mozilla/UniquePtr.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+namespace mozilla {
+
+static UniquePtr<MediaPacket> merge(UniquePtr<MediaPacket> a,
+ UniquePtr<MediaPacket> b) {
+ if (a && a->len() && b && b->len()) {
+ UniquePtr<uint8_t[]> data(new uint8_t[a->len() + b->len()]);
+ memcpy(data.get(), a->data(), a->len());
+ memcpy(data.get() + a->len(), b->data(), b->len());
+
+ UniquePtr<MediaPacket> merged(new MediaPacket);
+ merged->Take(std::move(data), a->len() + 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) override { return 0; }
+
+ virtual int sendto(const void* msg, size_t len, int flags,
+ const nr_transport_addr* to) override {
+ MOZ_CRASH();
+ return 0;
+ }
+
+ virtual int recvfrom(void* buf, size_t maxlen, size_t* len, int flags,
+ nr_transport_addr* from) override {
+ MOZ_CRASH();
+ return 0;
+ }
+
+ virtual int getaddr(nr_transport_addr* addrp) override {
+ MOZ_CRASH();
+ return 0;
+ }
+
+ virtual void close() override {}
+
+ virtual int connect(const nr_transport_addr* addr) override {
+ nr_transport_addr_copy(&connect_addr_, addr);
+ return 0;
+ }
+
+ virtual int listen(int backlog) override { return 0; }
+
+ virtual int accept(nr_transport_addr* addrp, nr_socket** sockp) override {
+ return 0;
+ }
+
+ virtual int write(const void* msg, size_t len, size_t* written) override {
+ size_t to_write = std::min(len, writable_);
+
+ if (to_write) {
+ UniquePtr<MediaPacket> msgbuf(new MediaPacket);
+ msgbuf->Copy(static_cast<const uint8_t*>(msg), to_write);
+ write_buffer_ = merge(std::move(write_buffer_), std::move(msgbuf));
+ }
+
+ *written = to_write;
+
+ return 0;
+ }
+
+ virtual int read(void* buf, size_t maxlen, size_t* len) override {
+ 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()) {
+ MediaPacket* newPacket = new MediaPacket;
+ newPacket->Copy(read_buffer_->data() + to_read,
+ read_buffer_->len() - to_read);
+ read_buffer_.reset(newPacket);
+ } 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) override {
+ EXPECT_EQ(nullptr, cb_);
+ cb_ = cb;
+ cb_arg_ = cb_arg;
+
+ return 0;
+ }
+
+ virtual int cancel(int how) override {
+ 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 MediaPacket);
+ read_buffer_->Copy(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, override);
+
+ private:
+ ~DummySocket() = default;
+
+ DISALLOW_COPY_ASSIGN(DummySocket);
+
+ size_t writable_; // Amount we allow someone to write.
+ UniquePtr<MediaPacket> write_buffer_;
+ size_t readable_; // Amount we allow someone to read.
+ UniquePtr<MediaPacket> read_buffer_;
+
+ NR_async_cb cb_;
+ void* cb_arg_;
+ nr_socket* self_;
+
+ nr_transport_addr connect_addr_;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/transport/test/gtest_ringbuffer_dumper.h b/dom/media/webrtc/transport/test/gtest_ringbuffer_dumper.h
new file mode 100644
index 0000000000..25e85c2155
--- /dev/null
+++ b/dom/media/webrtc/transport/test/gtest_ringbuffer_dumper.h
@@ -0,0 +1,78 @@
+/* -*- 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) override {
+ mozilla::SyncRunnable::DispatchToThread(
+ test_utils_->sts_target(),
+ WrapRunnable(this, &RingbufferDumper::ClearRingBuffer_s));
+ }
+
+ virtual void OnTestEnd(const ::testing::TestInfo& testInfo) override {
+ 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) override {
+ 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/dom/media/webrtc/transport/test/gtest_utils.h b/dom/media/webrtc/transport/test/gtest_utils.h
new file mode 100644
index 0000000000..40c2570ea1
--- /dev/null
+++ b/dom/media/webrtc/transport/test/gtest_utils.h
@@ -0,0 +1,201 @@
+/* -*- 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/dom/media/webrtc/transport/test/ice_unittest.cpp b/dom/media/webrtc/transport/test/ice_unittest.cpp
new file mode 100644
index 0000000000..adc3949dd8
--- /dev/null
+++ b/dom/media/webrtc/transport/test/ice_unittest.cpp
@@ -0,0 +1,4440 @@
+/* -*- 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 "ssl.h"
+
+#include "mozilla/Preferences.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+
+extern "C" {
+#include "r_types.h"
+#include "async_wait.h"
+#include "async_timer.h"
+#include "r_data.h"
+#include "util.h"
+#include "r_time.h"
+}
+
+#include "ice_ctx.h"
+#include "ice_peer_ctx.h"
+#include "ice_media_stream.h"
+
+#include "nricectx.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 "nsISocketFilter.h"
+#include "mozilla/net/DNS.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*)"stun.l.google.com");
+const std::string kBogusStunServerHostname(
+ (char*)"stun-server-nonexistent.invalid");
+const uint16_t kDefaultStunServerPort = 19305;
+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() {}
+
+ void SetUp() override {
+ MtransportTest::SetUp();
+
+ stun_server_hostname_ = kDefaultStunServerHostname;
+ // 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);
+ ASSERT_TRUE(!stun_server_address_.empty());
+ }
+
+ // 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 };
+
+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 (const auto& pair : pairs) {
+ if (pair.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,
+ const std::string& ufrag,
+ MtransportTestUtils* utils)
+ : peer_(peer),
+ stream_(stream),
+ candidate_(candidate),
+ ufrag_(ufrag),
+ 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_; }
+
+ 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_;
+ std::string ufrag_;
+ void* timer_handle_;
+ MtransportTestUtils* test_utils_;
+
+ DISALLOW_COPY_ASSIGN(SchedulableTrickleCandidate);
+};
+
+class IceTestPeer : public sigslot::has_slots<> {
+ public:
+ IceTestPeer(const std::string& name, MtransportTestUtils* utils, bool offerer,
+ const NrIceCtx::Config& config)
+ : name_(name),
+ ice_ctx_(NrIceCtx::Create(name)),
+ offerer_(offerer),
+ candidates_(),
+ stream_counter_(0),
+ 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),
+ simulate_ice_lite_(false),
+ nat_(new TestNat),
+ test_utils_(utils) {
+ ice_ctx_->SignalGatheringStateChange.connect(
+ this, &IceTestPeer::GatheringStateChange);
+ ice_ctx_->SignalConnectionStateChange.connect(
+ this, &IceTestPeer::ConnectionStateChange);
+
+ ice_ctx_->SetIceConfig(config);
+
+ consent_timestamp_.tv_sec = 0;
+ consent_timestamp_.tv_usec = 0;
+ int r = ice_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);
+ }
+
+ std::string MakeTransportId(size_t index) const {
+ char id[100];
+ snprintf(id, sizeof(id), "%s:stream%d", name_.c_str(), (int)index);
+ return id;
+ }
+
+ void SetIceCredentials_s(NrIceMediaStream& stream) {
+ static size_t counter = 0;
+ std::ostringstream prefix;
+ prefix << name_ << "-" << counter++;
+ std::string ufrag = prefix.str() + "-ufrag";
+ std::string pwd = prefix.str() + "-pwd";
+ if (mIceCredentials.count(stream.GetId())) {
+ mOldIceCredentials[stream.GetId()] = mIceCredentials[stream.GetId()];
+ }
+ mIceCredentials[stream.GetId()] = std::make_pair(ufrag, pwd);
+ stream.SetIceCredentials(ufrag, pwd);
+ }
+
+ void AddStream_s(int components) {
+ std::string id = MakeTransportId(stream_counter_++);
+
+ RefPtr<NrIceMediaStream> stream =
+ ice_ctx_->CreateStream(id, id, components);
+
+ ASSERT_TRUE(stream);
+ SetIceCredentials_s(*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_->DestroyStream(MakeTransportId(index));
+ }
+
+ void RemoveStream(size_t index) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::RemoveStream_s, index),
+ NS_DISPATCH_SYNC);
+ }
+
+ RefPtr<NrIceMediaStream> GetStream_s(size_t index) {
+ std::string id = MakeTransportId(index);
+ return ice_ctx_->GetStream(id);
+ }
+
+ 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_->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_->SetTurnServers(turn_servers)));
+ }
+
+ void SetTurnServers(const std::vector<NrIceTurnServer> servers) {
+ ASSERT_TRUE(NS_SUCCEEDED(ice_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_->SetResolver(fake_resolver_.AllocateResolver())));
+ }
+
+ void SetDNSResolver() {
+ ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
+ ASSERT_TRUE(
+ NS_SUCCEEDED(ice_ctx_->SetResolver(dns_resolver_->AllocateResolver())));
+ }
+
+ void Gather(bool default_route_only = false,
+ bool obfuscate_host_addresses = false) {
+ nsresult res;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::StartGathering,
+ default_route_only, obfuscate_host_addresses),
+ NS_DISPATCH_SYNC);
+
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ void SetCtxFlags(bool default_route_only) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(ice_ctx_, &NrIceCtx::SetCtxFlags, default_route_only),
+ NS_DISPATCH_SYNC);
+ }
+
+ nsTArray<NrIceStunAddr> GetStunAddrs() { return ice_ctx_->GetStunAddrs(); }
+
+ void SetStunAddrs(const nsTArray<NrIceStunAddr>& addrs) {
+ ice_ctx_->SetStunAddrs(addrs);
+ }
+
+ void UseNat() { nat_->enabled_ = true; }
+
+ void SetTimerDivider(int div) { ice_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_->GetGlobalAttributes());
+ if (simulate_ice_lite_) {
+ attrs.push_back("ice-lite");
+ }
+ return attrs;
+ }
+
+ std::vector<std::string> GetAttributes(size_t stream) {
+ std::vector<std::string> v;
+
+ RUN_ON_THREAD(
+ test_utils_->sts_target(),
+ WrapRunnableRet(&v, this, &IceTestPeer::GetAttributes_s, stream));
+
+ return v;
+ }
+
+ std::string FilterCandidate(const std::string& candidate) {
+ if (candidate_filter_) {
+ return candidate_filter_(candidate);
+ }
+ return candidate;
+ }
+
+ std::vector<std::string> GetAttributes_s(size_t index) {
+ std::vector<std::string> attributes;
+
+ auto stream = GetStream_s(index);
+ if (!stream) {
+ EXPECT_TRUE(false) << "No such stream " << index;
+ return attributes;
+ }
+
+ std::vector<std::string> attributes_in = stream->GetAttributes();
+
+ for (const auto& attribute : attributes_in) {
+ if (attribute.find("candidate:") != std::string::npos) {
+ std::string candidate(FilterCandidate(attribute));
+ if (!candidate.empty()) {
+ std::cerr << name_ << " Returning candidate: " << candidate
+ << std::endl;
+ attributes.push_back(candidate);
+ }
+ } else {
+ attributes.push_back(attribute);
+ }
+ }
+
+ return attributes;
+ }
+
+ 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> attributes = GetAttributes(stream);
+
+ int host_net = 0;
+ for (const auto& a : attributes) {
+ if (a.find("typ host") != std::string::npos) {
+ nr_transport_addr addr;
+ std::vector<std::string> tokens = split(a, ' ');
+ 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 index) {
+ auto media_stream = GetStream_s(index);
+ if (!media_stream) {
+ EXPECT_TRUE(false) << "No such stream " << index;
+ 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), NS_DISPATCH_SYNC);
+ }
+
+ void RestartIce_s() {
+ for (auto& stream : ice_ctx_->GetStreams()) {
+ SetIceCredentials_s(*stream);
+ }
+ // 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 RollbackIceRestart() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::RollbackIceRestart_s),
+ NS_DISPATCH_SYNC);
+ }
+
+ void RollbackIceRestart_s() {
+ for (auto& stream : ice_ctx_->GetStreams()) {
+ mIceCredentials[stream->GetId()] = mOldIceCredentials[stream->GetId()];
+ }
+ }
+
+ // 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_->ParseGlobalAttributes(remote->GetGlobalAttributes());
+ ASSERT_FALSE(remote->simulate_ice_lite_ &&
+ (ice_ctx_->GetControlling() == NrIceCtx::ICE_CONTROLLED));
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+
+ for (size_t i = 0; i < stream_counter_; ++i) {
+ auto aStream = GetStream_s(i);
+ if (aStream) {
+ std::vector<std::string> attributes = remote->GetAttributes(i);
+
+ for (auto it = attributes.begin(); it != attributes.end();) {
+ if (trickle_mode == TRICKLE_SIMULATE &&
+ it->find("candidate:") != std::string::npos) {
+ std::cerr << name_ << " Deferring remote candidate: " << *it
+ << std::endl;
+ attributes.erase(it);
+ } else {
+ std::cerr << name_ << " Adding remote attribute: " + *it
+ << std::endl;
+ ++it;
+ }
+ }
+ auto credentials = mIceCredentials[aStream->GetId()];
+ res = aStream->ConnectToPeer(credentials.first, credentials.second,
+ attributes);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+ }
+
+ if (start) {
+ ice_ctx_->SetControlling(offerer_ ? NrIceCtx::ICE_CONTROLLING
+ : NrIceCtx::ICE_CONTROLLED);
+ // Now start checks
+ res = ice_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|
+
+ std::vector<SchedulableTrickleCandidate*>& candidates =
+ ControlTrickle(stream);
+
+ for (auto& candidate : candidates) {
+ candidate->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> attributes = remote_->GetAttributes(stream);
+
+ for (const auto& attribute : attributes) {
+ if (attribute.find("candidate:") != std::string::npos) {
+ controlled_trickle_candidates_[stream].push_back(
+ new SchedulableTrickleCandidate(this, stream, attribute, "",
+ test_utils_));
+ }
+ }
+
+ return controlled_trickle_candidates_[stream];
+ }
+
+ nsresult TrickleCandidate_s(const std::string& candidate,
+ const std::string& ufrag, size_t index) {
+ auto stream = GetStream_s(index);
+ if (!stream) {
+ // stream might have gone away before the trickle timer popped
+ return NS_OK;
+ }
+ return stream->ParseTrickleCandidate(candidate, ufrag, "");
+ }
+
+ 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 (const auto& stream : ice_ctx_->GetStreams()) {
+ for (size_t j = 0; j < stream->components(); ++j) {
+ std::cerr << name_ << " Stream " << stream->GetId() << " component "
+ << j + 1 << std::endl;
+
+ UniquePtr<NrIceCandidate> local;
+ UniquePtr<NrIceCandidate> remote;
+
+ nsresult res = stream->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_, &NrIceCtx::destroy_peer_ctx), NS_DISPATCH_SYNC);
+ }
+
+ void Shutdown() {
+ std::cerr << name_ << " Shutdown" << std::endl;
+ shutting_down_ = true;
+ for (auto& controlled_trickle_candidate : controlled_trickle_candidates_) {
+ for (auto& cand : controlled_trickle_candidate.second) {
+ delete cand;
+ }
+ }
+
+ ice_ctx_->Destroy();
+ ice_ctx_ = nullptr;
+
+ if (remote_) {
+ remote_->UnsetRemote();
+ remote_ = nullptr;
+ }
+ }
+
+ void UnsetRemote() { remote_ = nullptr; }
+
+ void StartChecks() {
+ nsresult res;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(
+ &res, ice_ctx_, &NrIceCtx::SetControlling,
+ offerer_ ? NrIceCtx::ICE_CONTROLLING : NrIceCtx::ICE_CONTROLLED),
+ NS_DISPATCH_SYNC);
+ // Now start checks
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, ice_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_ << " ATTRIBUTES:" << std::endl;
+ for (const auto& stream : ice_ctx_->GetStreams()) {
+ std::cerr << "Stream " << stream->GetId() << std::endl;
+
+ std::vector<std::string> attributes = stream->GetAttributes();
+
+ for (const auto& attribute : attributes) {
+ std::cerr << attribute << std::endl;
+ }
+ }
+ std::cerr << std::endl;
+ }
+
+ void CandidateInitialized(NrIceMediaStream* stream,
+ const std::string& raw_candidate,
+ const std::string& ufrag,
+ const std::string& mdns_addr,
+ const std::string& actual_addr) {
+ 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 < stream_counter_; ++i) {
+ if (GetStream_s(i) == stream) {
+ ASSERT_GT(remote_->stream_counter_, i);
+ nsresult res = remote_->GetStream_s(i)->ParseTrickleCandidate(
+ candidate, ufrag, "");
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ return;
+ }
+ }
+ ADD_FAILURE() << "No matching stream found for " << stream;
+ }
+ }
+
+ nsresult GetCandidatePairs_s(size_t stream_index,
+ std::vector<NrIceCandidatePair>* pairs) {
+ MOZ_ASSERT(pairs);
+ auto stream = GetStream_s(stream_index);
+ if (!stream) {
+ // Is there a better error for "no such index"?
+ ADD_FAILURE() << "No such media stream index: " << stream_index;
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return stream->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 (auto& pair : pairs) {
+ DumpCandidatePair(pair);
+ }
+ std::cerr << "]" << std::endl;
+ }
+
+ void DumpCandidatePairs_s() {
+ std::cerr << "Dumping candidate pairs for all streams [" << std::endl;
+ for (const auto& stream : ice_ctx_->GetStreams()) {
+ DumpCandidatePairs_s(stream.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;
+ }
+ 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 (auto& added_pair : added_pairs) {
+ std::cerr << "Found new candidate pair." << std::endl;
+ DumpCandidatePair(added_pair);
+ }
+
+ for (auto& removed_pair : removed_pairs) {
+ std::cerr << "Pre-existing candidate pair is now missing:" << std::endl;
+ DumpCandidatePair(removed_pair);
+ }
+
+ 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) {
+ auto media_stream = GetStream_s(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) {
+ auto media_stream = GetStream_s(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,
+ const std::string& mdns_addr) {
+ auto media_stream = GetStream_s(i);
+ ASSERT_TRUE(media_stream.get())
+ << "No such stream " << i;
+ media_stream->ParseTrickleCandidate(candidate, "", mdns_addr);
+ }
+
+ void ParseCandidate(size_t i, const std::string& candidate,
+ const std::string& mdns_addr) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::ParseCandidate_s, i, candidate,
+ mdns_addr),
+ NS_DISPATCH_SYNC);
+ }
+
+ void DisableComponent_s(size_t index, int component_id) {
+ ASSERT_LT(index, stream_counter_);
+ auto stream = GetStream_s(index);
+ ASSERT_TRUE(stream.get())
+ << "No such stream " << index;
+ nsresult res = 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 index, int component_id,
+ ConsentStatus status) {
+ ASSERT_LT(index, stream_counter_);
+ auto stream = GetStream_s(index);
+ ASSERT_TRUE(stream.get())
+ << "No such stream " << index;
+ bool can_send;
+ struct timeval timestamp;
+ nsresult res =
+ 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);
+ }
+
+ void ChangeNetworkState_s(bool online) {
+ ice_ctx_->UpdateNetworkState(online);
+ }
+
+ void ChangeNetworkStateToOffline() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::ChangeNetworkState_s, false),
+ NS_DISPATCH_SYNC);
+ }
+
+ void ChangeNetworkStateToOnline() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::ChangeNetworkState_s, true),
+ NS_DISPATCH_SYNC);
+ }
+
+ void SetControlling(NrIceCtx::Controlling controlling) {
+ nsresult res;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::SetControlling, controlling),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ NrIceCtx::Controlling GetControlling() { return ice_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_->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 index, NrIceCandidate* cand) {
+ return GetStream_s(index)->GetDefaultCandidate(1, cand);
+ }
+
+ private:
+ std::string name_;
+ RefPtr<NrIceCtx> ice_ctx_;
+ bool offerer_;
+ 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_;
+ std::map<std::string, std::pair<std::string, std::string>> mIceCredentials;
+ std::map<std::string, std::pair<std::string, std::string>> mOldIceCredentials;
+ size_t stream_counter_;
+ 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_;
+ bool simulate_ice_lite_;
+ RefPtr<mozilla::TestNat> nat_;
+ MtransportTestUtils* test_utils_;
+};
+
+void SchedulableTrickleCandidate::Trickle() {
+ timer_handle_ = nullptr;
+ nsresult res = peer_->TrickleCandidate_s(candidate_, ufrag_, 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() {
+ if (!peer_) {
+ peer_ =
+ MakeUnique<IceTestPeer>("P1", test_utils_, true, NrIceCtx::Config());
+ }
+ }
+
+ void Gather(unsigned int waitTime = kDefaultTimeout,
+ bool default_route_only = false,
+ bool obfuscate_host_addresses = false) {
+ EnsurePeer();
+ peer_->Gather(default_route_only, obfuscate_host_addresses);
+
+ 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()));
+
+ if (family == AF_INET6 && !fqdn.empty()) {
+ stun_servers->back().SetUseIPv6IfFqdn();
+ }
+ }
+
+ 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();
+ 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();
+ 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> attributes = peer_->GetAttributes(stream);
+ for (auto& attribute : attributes) {
+ if (std::string::npos != attribute.find(match)) {
+ if (!match2.length() || std::string::npos != attribute.find(match2)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void DumpAttributes(unsigned int stream) {
+ std::vector<std::string> attributes = peer_->GetAttributes(stream);
+
+ std::cerr << "Attributes for stream " << stream << "->" << attributes.size()
+ << std::endl;
+
+ for (const auto& a : attributes) {
+ std::cerr << "Attribute: " << a << 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();
+ p1_->AddStream(components);
+ p2_->AddStream(components);
+ }
+
+ void RemoveStream(size_t index) {
+ p1_->RemoveStream(index);
+ p2_->RemoveStream(index);
+ }
+
+ void Init(bool setup_stun_servers = true,
+ NrIceCtx::Policy ice_policy = NrIceCtx::ICE_POLICY_ALL) {
+ if (initted_) {
+ return;
+ }
+
+ NrIceCtx::Config config;
+ config.mPolicy = ice_policy;
+
+ p1_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+ p2_ = MakeUnique<IceTestPeer>("P2", test_utils_, false, config);
+ 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));
+
+ peer->SetStunServers(stun_servers);
+ }
+ }
+
+ bool Gather(unsigned int waitTime = kDefaultTimeout,
+ bool default_route_only = false) {
+ Init();
+
+ 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,
+ TrickleMode mode = TRICKLE_NONE) {
+ 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, mode);
+ caller->Connect(callee, mode);
+
+ if (mode != TRICKLE_SIMULATE) {
+ ASSERT_TRUE_WAIT(caller->ice_connected() && callee->ice_connected(),
+ kDefaultTimeout);
+ ASSERT_TRUE(caller->ready_ct() >= 1 && callee->ready_ct() >= 1);
+ 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 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();
+
+ if (expect_tx_failure) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(p1, &IceTestPeer::SendFailure, 0, 1), NS_DISPATCH_SYNC);
+ ASSERT_EQ(previousSent, p1->sent());
+ } else {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(p1, &IceTestPeer::SendPacket, 0, 1,
+ reinterpret_cast<const unsigned char*>("TEST"), 4),
+ NS_DISPATCH_SYNC);
+ 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 - 1);
+ local_addr.addr.ifname[MAXIFNAME - 1] = '\0';
+
+ 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();
+
+ NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
+
+ // Set up enough of the ICE ctx to allow the packet filter to work
+ ice_ctx_ = NrIceCtx::Create("test");
+
+ 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<NrIceCtx> ice_ctx_;
+};
+} // end namespace
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerHostnameNoResolver) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->AddStream(1);
+ Gather();
+}
+
+// Disabled because google isn't running any TCP stun servers right now
+TEST_F(WebRtcIceGatherTest,
+ DISABLED_TestGatherFakeStunServerTcpHostnameNoResolver) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerIpAddress) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort);
+ peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_);
+ peer_->AddStream(1);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherStunServerIpAddressNoHost) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ NrIceCtx::Config config;
+ config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST;
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+ 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;
+ }
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_);
+ peer_->AddStream(1);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherFakeStunBogusHostname) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort);
+ peer_->SetFakeResolver(stun_server_address_, stun_server_hostname_);
+ peer_->AddStream(1);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunServerIpAddress) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ // A srflx candidate is considered redundant and discarded if its address
+ // equals that of a host candidate. (Frequently, a srflx candidate and a host
+ // candidate have equal addresses when the agent is not behind a NAT.) So set
+ // ICE_POLICY_NO_HOST here to ensure that a srflx candidate is not falsely
+ // discarded in this test.
+ NrIceCtx::Config config;
+ config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST;
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+
+ peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort);
+ peer_->SetDNSResolver();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "typ srflx raddr"));
+}
+
+// Disabled because google isn't running any TCP stun servers right now
+TEST_F(WebRtcIceGatherTest, DISABLED_TestGatherDNSStunServerIpAddressTcp) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_address_, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ peer_->SetDNSResolver();
+ peer_->AddStream(1);
+ 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;
+ }
+
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ // A srflx candidate is considered redundant and discarded if its address
+ // equals that of a host candidate. (Frequently, a srflx candidate and a host
+ // candidate have equal addresses when the agent is not behind a NAT.) So set
+ // ICE_POLICY_NO_HOST here to ensure that a srflx candidate is not falsely
+ // discarded in this test.
+ NrIceCtx::Config config;
+ config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST;
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->SetDNSResolver();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "typ srflx raddr"));
+}
+
+// Disabled because google isn't running any TCP stun servers right now
+TEST_F(WebRtcIceGatherTest, DISABLED_TestGatherDNSStunServerHostnameTcp) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ peer_->SetDNSResolver();
+ peer_->AddStream(1);
+ 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 "));
+}
+
+// Disabled because google isn't running any TCP stun servers right now
+TEST_F(WebRtcIceGatherTest,
+ DISABLED_TestGatherDNSStunServerHostnameBothUdpTcp) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ std::vector<NrIceStunServer> stun_servers;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ 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();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+// Disabled because google isn't running any TCP stun servers right now
+TEST_F(WebRtcIceGatherTest,
+ DISABLED_TestGatherDNSStunServerIpAddressBothUdpTcp) {
+ if (stun_server_address_.empty()) {
+ return;
+ }
+
+ std::vector<NrIceStunServer> stun_servers;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ 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();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDNSStunBogusHostname) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort);
+ peer_->SetDNSResolver();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+}
+
+// Disabled because google isn't running any TCP stun servers right now
+TEST_F(WebRtcIceGatherTest, DISABLED_TestGatherDNSStunBogusHostnameTcp) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(kBogusStunServerHostname, kDefaultStunServerPort,
+ kNrIceTransportTcp);
+ peer_->SetDNSResolver();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " TCP "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestDefaultCandidate) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->AddStream(1);
+ Gather();
+ NrIceCandidate default_candidate;
+ ASSERT_TRUE(NS_SUCCEEDED(peer_->GetDefaultCandidate(0, &default_candidate)));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherTurn) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ if (turn_server_.empty()) return;
+ peer_->SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_, kNrIceTransportUdp);
+ peer_->AddStream(1);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherTurnTcp) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ if (turn_server_.empty()) return;
+ peer_->SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_, kNrIceTransportTcp);
+ peer_->AddStream(1);
+ Gather();
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherDisableComponent) {
+ if (stun_server_hostname_.empty()) {
+ return;
+ }
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->SetStunServer(stun_server_hostname_, kDefaultStunServerPort);
+ peer_->AddStream(1);
+ peer_->AddStream(2);
+ peer_->DisableComponent(1, 2);
+ Gather();
+ std::vector<std::string> attributes = peer_->GetAttributes(1);
+
+ for (auto& attribute : attributes) {
+ if (attribute.find("candidate:") != std::string::npos) {
+ size_t sp1 = attribute.find(' ');
+ ASSERT_EQ(0, attribute.compare(sp1 + 1, 1, "1", 1));
+ }
+ }
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherVerifyNoLoopback) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "127.0.0.1"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherAllowLoopback) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ config.mAllowLoopback = true;
+ NrIceCtx::InitializeGlobals(config);
+
+ // Set up peer with loopback allowed.
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, NrIceCtx::Config());
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "127.0.0.1"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestGatherTcpDisabledNoStun) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " TCP "));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " UDP "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunServer) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("192.0.2.133", 3333);
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.133 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunTcpServer) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunTcpServerWithResponse("192.0.2.233", 3333);
+ peer_->AddStream(1);
+ 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;
+ }
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("beef::", 3333);
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " beef:: 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunServerFQDN) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("192.0.2.133", 3333, "stun.example.com");
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " 192.0.2.133 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, VerifyTestStunServerV6FQDN) {
+ if (!TestStunServer::GetInstance(AF_INET6)) {
+ // No V6 addresses
+ return;
+ }
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("beef::", 3333, "stun.example.com");
+ peer_->AddStream(1);
+ Gather();
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, " beef:: 3333 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsWildcardAddr) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("0.0.0.0", 3333);
+ peer_->AddStream(1);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 0.0.0.0 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsWildcardAddrV6) {
+ if (!TestStunServer::GetInstance(AF_INET6)) {
+ // No V6 addresses
+ return;
+ }
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("::", 3333);
+ peer_->AddStream(1);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " :: "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsPort0) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("192.0.2.133", 0);
+ peer_->AddStream(1);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 192.0.2.133 0 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsLoopbackAddr) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("127.0.0.133", 3333);
+ peer_->AddStream(1);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " 127.0.0.133 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerReturnsLoopbackAddrV6) {
+ if (!TestStunServer::GetInstance(AF_INET6)) {
+ // No V6 addresses
+ return;
+ }
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("::1", 3333);
+ peer_->AddStream(1);
+ Gather(kDefaultTimeout * 3);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, " ::1 "));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunServerTrickle) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpServerWithResponse("192.0.2.1", 3333);
+ peer_->AddStream(1);
+ 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) {
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ NrIceCtx::Config config;
+ config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST;
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+ UseFakeStunUdpServerWithResponse("192.0.2.1", 3333);
+ peer_->AddStream(1);
+ Gather(0);
+ WaitForGather();
+ DumpAttributes(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) {
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ NrIceCtx::Config config;
+ config.mPolicy = NrIceCtx::ICE_POLICY_NO_HOST;
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+ UseTestStunServer();
+ peer_->AddStream(1);
+ Gather(0);
+ WaitForGather();
+ DumpAttributes(0);
+ ASSERT_FALSE(StreamHasMatchingCandidate(0, "host"));
+ ASSERT_TRUE(StreamHasMatchingCandidate(0, "srflx"));
+}
+
+// Test that srflx candidate is discarded in non-NATted environment if host
+// address obfuscation is not enabled.
+TEST_F(WebRtcIceGatherTest,
+ TestSrflxCandidateDiscardedWithObfuscateHostAddressesNotEnabled) {
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ NrIceCtx::Config config;
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+ UseTestStunServer();
+ peer_->AddStream(1);
+ Gather(0, false, false);
+ WaitForGather();
+ DumpAttributes(0);
+ EXPECT_TRUE(StreamHasMatchingCandidate(0, "host"));
+ EXPECT_FALSE(StreamHasMatchingCandidate(0, "srflx"));
+}
+
+// Test that srflx candidate is generated in non-NATted environment if host
+// address obfuscation is enabled.
+TEST_F(WebRtcIceGatherTest,
+ TestSrflxCandidateGeneratedWithObfuscateHostAddressesEnabled) {
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ NrIceCtx::Config config;
+ peer_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+ UseTestStunServer();
+ peer_->AddStream(1);
+ Gather(0, false, true);
+ WaitForGather();
+ DumpAttributes(0);
+ EXPECT_TRUE(StreamHasMatchingCandidate(0, "host"));
+ EXPECT_TRUE(StreamHasMatchingCandidate(0, "srflx"));
+}
+
+TEST_F(WebRtcIceGatherTest, TestStunTcpServerTrickle) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunTcpServerWithResponse("192.0.3.1", 3333);
+ TestStunTcpServer::GetInstance(AF_INET)->SetDelay(500);
+ peer_->AddStream(1);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ UseFakeStunUdpTcpServersWithResponse("192.0.2.1", 3333, "192.0.3.1", 3333);
+ TestStunServer::GetInstance(AF_INET)->SetDropInitialPackets(3);
+ TestStunTcpServer::GetInstance(AF_INET)->SetDelay(500);
+ peer_->AddStream(1);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->AddStream(1);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ EnsurePeer();
+ peer_->AddStream(1);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherTcp) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherAutoPrioritize) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnect) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectRestartIce) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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, NrIceCtx::Config());
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ ConnectCallerAndCallee(p3_.get(), p2_.get(), TRICKLE_SIMULATE);
+ SendReceive(p1_.get(), p2_.get()); // p1 and p2 are still connected
+ SendReceive(p3_.get(), p2_.get(), true, true); // p3 and p2 not yet connected
+ p2_->SimulateTrickle(0);
+ p3_->SimulateTrickle(0);
+ ASSERT_TRUE_WAIT(p3_->is_ready(0), kDefaultTimeout);
+ ASSERT_TRUE_WAIT(p2_->is_ready(0), kDefaultTimeout);
+ SendReceive(p1_.get(), p2_.get(), false, true); // p1 and p2 not connected
+ SendReceive(p3_.get(), p2_.get()); // p3 and p2 are now connected
+
+ p3_ = nullptr;
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectRestartIceThenAbort) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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, NrIceCtx::Config());
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ p2_->RollbackIceRestart();
+ p2_->Connect(p1_.get(), TRICKLE_NONE);
+ SendReceive(p1_.get(), p2_.get());
+ p3_ = nullptr;
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectIceRestartRoleConflict) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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());
+ p2_->SetControlling(NrIceCtx::ICE_CONTROLLED);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p2_->GetControlling())
+ << "ICE restart should not allow role to change, unless ice-lite happens";
+
+ mozilla::UniquePtr<IceTestPeer> p3_;
+ p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, NrIceCtx::Config());
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+ // Set control role for p3 accordingly (with role conflict)
+ p3_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLING, p3_->GetControlling());
+
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ ConnectCallerAndCallee(p3_.get(), p2_.get());
+ auto p2role = p2_->GetControlling();
+ ASSERT_NE(p2role, p3_->GetControlling()) << "Conflict should be resolved";
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p1_->GetControlling())
+ << "P1 should be unaffected by role conflict";
+
+ // And again we are not allowed to switch roles at this point any more
+ p1_->SetControlling(NrIceCtx::ICE_CONTROLLING);
+ ASSERT_EQ(NrIceCtx::ICE_CONTROLLED, p1_->GetControlling());
+ p3_->SetControlling(p2role);
+ ASSERT_NE(p2role, p3_->GetControlling());
+
+ p3_ = nullptr;
+}
+
+TEST_F(WebRtcIceConnectTest,
+ TestIceRestartWithMultipleInterfacesAndUserStartingScreenSharing) {
+ const char* FAKE_WIFI_ADDR = "10.0.0.1";
+ const char* FAKE_WIFI_IF_NAME = "wlan9";
+
+ // prepare a fake wifi interface
+ nr_local_addr wifi_addr;
+ wifi_addr.interface.type = NR_INTERFACE_TYPE_WIFI;
+ wifi_addr.interface.estimated_speed = 1000;
+
+ int r = nr_str_port_to_transport_addr(FAKE_WIFI_ADDR, 0, IPPROTO_UDP,
+ &(wifi_addr.addr));
+ ASSERT_EQ(0, r);
+ strncpy(wifi_addr.addr.ifname, FAKE_WIFI_IF_NAME, MAXIFNAME);
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ // setup initial ICE connection between p1_ and p2_
+ UseNat();
+ AddStream(1);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ ASSERT_TRUE(Gather(kDefaultTimeout, true));
+ Connect();
+
+ // verify the connection is working
+ SendReceive(p1_.get(), p2_.get());
+
+ // simulate user accepting permissions for screen sharing
+ p2_->SetCtxFlags(false);
+
+ // and having an additional non-default interface
+ nsTArray<NrIceStunAddr> stunAddr = p2_->GetStunAddrs();
+ stunAddr.InsertElementAt(0, NrIceStunAddr(&wifi_addr));
+ p2_->SetStunAddrs(stunAddr);
+
+ std::cout << "-------------------------------------------------" << std::endl;
+
+ // now restart ICE
+ p2_->RestartIce();
+ ASSERT_FALSE(p2_->gathering_complete());
+
+ // verify that we can successfully gather candidates
+ p2_->Gather();
+ EXPECT_TRUE_WAIT(p2_->gathering_complete(), kDefaultTimeout);
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTcp) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ Init(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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ config.mAllowLoopback = true;
+ NrIceCtx::InitializeGlobals(config);
+ Init(false);
+ AddStream(1);
+ SetCandidateFilter(IsLoopbackCandidate);
+ ASSERT_TRUE(Gather());
+ SetExpectedRemoteCandidateAddr("127.0.0.1");
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectBothControllingP1Wins) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ p1_->SimulateIceLite();
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestTrickleBothControllingP1Wins) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ p1_->SimulateIceLite();
+ ConnectTrickle();
+ SimulateTrickle(0);
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherFullCone) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherFullConeAutoPrioritize) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ Init();
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectFullCone) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ AddStream(1);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectNoNatNoHost) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ Init(false, NrIceCtx::ICE_POLICY_NO_HOST);
+ 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);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectFullConeNoHost) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ Init(false, NrIceCtx::ICE_POLICY_NO_HOST);
+ UseTestStunServer();
+ SetExpectedTypes(NrIceCandidate::Type::ICE_SERVER_REFLEXIVE,
+ NrIceCandidate::Type::ICE_SERVER_REFLEXIVE);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestGatherAddressRestrictedCone) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ SetFilteringType(TestNat::ADDRESS_DEPENDENT);
+ SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectAddressRestrictedCone) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ SetFilteringType(TestNat::PORT_DEPENDENT);
+ SetMappingType(TestNat::ENDPOINT_INDEPENDENT);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectPortRestrictedCone) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ SetFilteringType(TestNat::PORT_DEPENDENT);
+ SetMappingType(TestNat::PORT_DEPENDENT);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectSymmetricNat) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ SetFilteringType(TestNat::PORT_DEPENDENT);
+ SetMappingType(TestNat::PORT_DEPENDENT);
+ 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_);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectSymmetricNatAndNoNat) {
+ {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ }
+
+ NrIceCtx::Config config;
+ p1_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, config);
+ p1_->UseNat();
+ p1_->SetFilteringType(TestNat::PORT_DEPENDENT);
+ p1_->SetMappingType(TestNat::PORT_DEPENDENT);
+
+ p2_ = MakeUnique<IceTestPeer>("P2", test_utils_, false, config);
+ 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;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ BlockUdp();
+ 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);
+ AddStream(1);
+ // 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;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ UseNat();
+ BlockUdp();
+ 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);
+ AddStream(1);
+ ASSERT_TRUE(Gather(kDefaultTimeout * 3));
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTwoComponents) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(2);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTwoComponentsDisableSecond) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(2);
+ ASSERT_TRUE(Gather());
+ p1_->DisableComponent(0, 2);
+ p2_->DisableComponent(0, 2);
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectP2();
+ PR_Sleep(1000);
+ ConnectP1();
+ WaitForConnectedStreams();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1Trickle) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectP2();
+ PR_Sleep(1000);
+ ConnectP1(TRICKLE_SIMULATE);
+ SimulateTrickleP1(0);
+ WaitForConnectedStreams();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectP2ThenP1TrickleTwoComponents) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTrickleOneStreamOneComponent) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ SimulateTrickle(0);
+ WaitForConnected(1000);
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTrickleTwoStreamsOneComponent) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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& candidate : candidates) {
+ if (candidate->IsRelay()) {
+ candidate->Schedule(ms);
+ } else {
+ candidate->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:
+ NR_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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ DropTrickleCandidates(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, P2NoTrickle) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ DropTrickleCandidates(p2_->ControlTrickle(0));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, RemoveAndAddStream) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ AddStream(1);
+ RemoveStream(0);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(1));
+ RealisticTrickleDelay(p2_->ControlTrickle(1));
+ WaitForConnected(1000);
+}
+
+TEST_F(WebRtcIceConnectTest, RemoveStreamDuringConnect) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestSendReceiveTcp) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsTcpSoCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsent) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ SetupAndCheckConsent();
+ PR_Sleep(1500);
+ AssertConsentRefresh();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsentTcp) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = true;
+ NrIceCtx::InitializeGlobals(config);
+ Init();
+ AddStream(1);
+ SetCandidateFilter(IsTcpCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
+ NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
+ SetupAndCheckConsent();
+ PR_Sleep(1500);
+ AssertConsentRefresh();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsentIntermittent) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ SetupAndCheckConsent();
+ p1_->SetBlockStun(true);
+ p2_->SetBlockStun(true);
+ WaitForDisconnected();
+ AssertConsentRefresh(CONSENT_STALE);
+ SendReceive();
+ WaitForFailed();
+ AssertConsentRefresh(CONSENT_EXPIRED);
+ SendFailure();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConsentDelayed) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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(200);
+ p2_->SetStunResponseDelay(200);
+ PR_Sleep(1000);
+ AssertConsentRefresh();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestNetworkForcedOfflineAndRecovery) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ SetupAndCheckConsent();
+ p1_->ChangeNetworkStateToOffline();
+ ASSERT_TRUE_WAIT(p1_->ice_connected() == 0, kDefaultTimeout);
+ // Next round of consent check should switch it back to online
+ ASSERT_TRUE_WAIT(p1_->ice_connected(), kDefaultTimeout);
+}
+
+TEST_F(WebRtcIceConnectTest, TestNetworkForcedOfflineTwice) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ SetupAndCheckConsent();
+ p2_->ChangeNetworkStateToOffline();
+ ASSERT_TRUE_WAIT(p2_->ice_connected() == 0, kDefaultTimeout);
+ p2_->ChangeNetworkStateToOffline();
+ ASSERT_TRUE_WAIT(p2_->ice_connected() == 0, kDefaultTimeout);
+}
+
+TEST_F(WebRtcIceConnectTest, TestNetworkOnlineDoesntChangeState) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ SetupAndCheckConsent();
+ p2_->ChangeNetworkStateToOnline();
+ ASSERT_TRUE(p2_->ice_connected());
+ PR_Sleep(1500);
+ p2_->ChangeNetworkStateToOnline();
+ ASSERT_TRUE(p2_->ice_connected());
+}
+
+TEST_F(WebRtcIceConnectTest, TestNetworkOnlineTriggersConsent) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ // Let's emulate audio + video w/o rtcp-mux
+ AddStream(2);
+ AddStream(2);
+ SetupAndCheckConsent();
+ p1_->ChangeNetworkStateToOffline();
+ p1_->SetBlockStun(true);
+ ASSERT_TRUE_WAIT(p1_->ice_connected() == 0, kDefaultTimeout);
+ PR_Sleep(1500);
+ ASSERT_TRUE(p1_->ice_connected() == 0);
+ p1_->SetBlockStun(false);
+ p1_->ChangeNetworkStateToOnline();
+ ASSERT_TRUE_WAIT(p1_->ice_connected(), 500);
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurn) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithDelay) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_);
+ SetCandidateFilter(SabotageHostCandidateAndDropReflexive);
+ AddStream(1);
+ p1_->Gather();
+ PR_Sleep(500);
+ p2_->Gather();
+ ConnectTrickle(TRICKLE_REAL);
+ WaitForGather();
+ WaitForConnectedStreams();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelay) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ RealisticTrickleDelay(p2_->ControlTrickle(0));
+
+ WaitForConnected();
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithNormalTrickleDelayOneSided) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectTrickle();
+ RealisticTrickleDelay(p1_->ControlTrickle(0));
+ p2_->SimulateTrickle(0);
+
+ WaitForConnected();
+ AssertCheckingReached();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnWithLargeTrickleDelay) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_);
+ SetCandidateFilter(SabotageHostCandidateAndDropReflexive);
+ AddStream(1);
+ 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;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_, kNrIceTransportTcp);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnOnly) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsRelayCandidate);
+ SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED);
+ Connect();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectTurnTcpOnly) {
+ if (turn_server_.empty()) return;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_, kNrIceTransportTcp);
+ AddStream(1);
+ 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;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_);
+ AddStream(1);
+ 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;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ SetTurnServer(turn_server_, kDefaultStunServerPort, turn_user_,
+ turn_password_, kNrIceTransportTcp);
+ AddStream(1);
+ 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;
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ SetCandidateFilter(IsRelayCandidate);
+ // UDP is preferred.
+ SetExpectedTypes(NrIceCandidate::Type::ICE_RELAYED,
+ NrIceCandidate::Type::ICE_RELAYED, kNrIceTransportUdp);
+ Connect();
+ SendReceive();
+}
+
+TEST_F(WebRtcIceConnectTest, TestConnectShutdownOneSide) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ ASSERT_TRUE(Gather());
+ ConnectThenDelete();
+}
+
+TEST_F(WebRtcIceConnectTest, TestPollCandPairsBeforeConnect) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ Init(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;
+ }
+
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ Init(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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ 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) {
+ 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) {
+ std::deque<std::string> logs;
+ std::string substring("CAND-PAIR(");
+ substring += p.codeword;
+ RLogConnector::GetInstance()->Filter(substring, 0, &logs);
+ ASSERT_NE(0U, logs.size());
+ }
+}
+
+// Verify that a bogus candidate doesn't cause crashes on the
+// main thread. See bug 856433.
+TEST_F(WebRtcIceConnectTest, TestBogusCandidate) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ Gather();
+ ConnectTrickle();
+ p1_->ParseCandidate(0, kBogusIceCandidate, "");
+
+ std::vector<NrIceCandidatePair> pairs;
+ nsresult res = p1_->GetCandidatePairs(0, &pairs);
+ ASSERT_EQ(NS_OK, res);
+ ASSERT_EQ(0U, pairs.size());
+}
+
+TEST_F(WebRtcIceConnectTest, TestNonMDNSCandidate) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ Gather();
+ ConnectTrickle();
+ p1_->ParseCandidate(0, kUnreachableHostIceCandidate, "");
+
+ std::vector<NrIceCandidatePair> pairs;
+ nsresult res = p1_->GetCandidatePairs(0, &pairs);
+ ASSERT_EQ(NS_OK, res);
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(pairs[0].remote.mdns_addr, "");
+}
+
+TEST_F(WebRtcIceConnectTest, TestMDNSCandidate) {
+ NrIceCtx::GlobalConfig config;
+ config.mTcpEnabled = false;
+ NrIceCtx::InitializeGlobals(config);
+ AddStream(1);
+ Gather();
+ ConnectTrickle();
+ p1_->ParseCandidate(0, kUnreachableHostIceCandidate, "host.local");
+
+ std::vector<NrIceCandidatePair> pairs;
+ nsresult res = p1_->GetCandidatePairs(0, &pairs);
+ ASSERT_EQ(NS_OK, res);
+ ASSERT_EQ(1U, pairs.size());
+ ASSERT_EQ(pairs[0].remote.mdns_addr, "host.local");
+}
+
+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(nullptr, &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(nullptr, &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, TestRecvStunBindingRequestWithoutAPendingId) {
+ nr_stun_message* msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &msg));
+
+ msg->header.id.octet[0] = 1;
+ 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.id.octet[0] = 1;
+ 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,
+ TestRecvStunPacketWithoutAPendingIdTcpFramed) {
+ nr_stun_message* msg;
+ ASSERT_EQ(0, nr_stun_build_req_no_auth(nullptr, &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(nullptr, &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(nullptr, &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(nullptr, &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(nullptr, &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(nullptr, &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/dom/media/webrtc/transport/test/moz.build b/dom/media/webrtc/transport/test/moz.build
new file mode 100644
index 0000000000..69d3a587a5
--- /dev/null
+++ b/dom/media/webrtc/transport/test/moz.build
@@ -0,0 +1,104 @@
+# -*- 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")
+
+if CONFIG["OS_TARGET"] != "WINNT":
+
+ if CONFIG["OS_TARGET"] != "Android":
+ SOURCES += [
+ "ice_unittest.cpp",
+ ]
+
+ SOURCES += [
+ "buffered_stun_socket_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_ice_unittest.cpp",
+ "test_nr_socket_unittest.cpp",
+ "TestSyncRunnable.cpp",
+ "transport_unittests.cpp",
+ "turn_unittest.cpp",
+ "webrtcproxychannel_unittest.cpp",
+ ]
+
+ if CONFIG["MOZ_SCTP"]:
+ SOURCES += [
+ "sctp_unittest.cpp",
+ ]
+
+
+for var in ("HAVE_STRDUP", "NR_SOCKET_IS_VOID_PTR", "SCTP_DEBUG"):
+ DEFINES[var] = True
+
+if CONFIG["OS_TARGET"] == "Android":
+ DEFINES["LINUX"] = True
+ DEFINES["ANDROID"] = True
+ LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include",
+ ]
+
+if CONFIG["OS_TARGET"] == "Linux":
+ DEFINES["LINUX"] = True
+ LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include",
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+ LOCAL_INCLUDES += [
+ "/dom/media/webrtc/transport/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 += [
+ "/dom/media/webrtc/transport/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 += [
+ "/dom/media/webrtc/transport/",
+ "/dom/media/webrtc/transport/third_party/",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/crypto",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/ice",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/net",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/stun",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/util",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/event",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/log",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/plugin",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/registry",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/share",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/stats",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/util/",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr",
+ "/netwerk/sctp/src/",
+ "/xpcom/tests/",
+]
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/dom/media/webrtc/transport/test/mtransport_test_utils.h b/dom/media/webrtc/transport/test/mtransport_test_utils.h
new file mode 100644
index 0000000000..d519508286
--- /dev/null
+++ b/dom/media/webrtc/transport/test/mtransport_test_utils.h
@@ -0,0 +1,47 @@
+/* -*- 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 "nsISerialEventTarget.h"
+#include "nsPISocketTransportService.h"
+#include "nsServiceManagerUtils.h"
+
+class MtransportTestUtils {
+ public:
+ MtransportTestUtils() { InitServices(); }
+
+ ~MtransportTestUtils() = default;
+
+ 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));
+ }
+
+ nsISerialEventTarget* sts_target() { return sts_target_; }
+
+ private:
+ nsCOMPtr<nsISerialEventTarget> 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/dom/media/webrtc/transport/test/multi_tcp_socket_unittest.cpp b/dom/media/webrtc/transport/test/multi_tcp_socket_unittest.cpp
new file mode 100644
index 0000000000..8b49b70fde
--- /dev/null
+++ b/dom/media/webrtc/transport/test/multi_tcp_socket_unittest.cpp
@@ -0,0 +1,515 @@
+/* -*- 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 "pk11pub.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "transport_addr.h"
+#include "nr_socket_multi_tcp.h"
+}
+
+#include "stunserver.h"
+
+#include "nricectx.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();
+
+ NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
+ ice_ctx_ = NrIceCtx::Create("stun");
+
+ 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 (auto& sock : socks) {
+ nr_socket_destroy(&sock);
+ }
+ }
+
+ 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_->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(), nullptr, &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<NrIceCtx> ice_ctx_;
+};
+} // namespace
+
+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/dom/media/webrtc/transport/test/nrappkit_unittest.cpp b/dom/media/webrtc/transport/test/nrappkit_unittest.cpp
new file mode 100644
index 0000000000..b8633e7930
--- /dev/null
+++ b/dom/media/webrtc/transport/test/nrappkit_unittest.cpp
@@ -0,0 +1,126 @@
+
+/* -*- 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>
+
+// 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() = default;
+
+ 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_;
+};
+} // namespace
+
+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/dom/media/webrtc/transport/test/proxy_tunnel_socket_unittest.cpp b/dom/media/webrtc/transport/test/proxy_tunnel_socket_unittest.cpp
new file mode 100644
index 0000000000..1b54126dd6
--- /dev/null
+++ b/dom/media/webrtc/transport/test/proxy_tunnel_socket_unittest.cpp
@@ -0,0 +1,277 @@
+/* -*- 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 <vector>
+#include <numeric>
+
+#include "nr_socket_tcp.h"
+#include "WebrtcTCPSocketWrapper.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+// update TestReadMultipleSizes if you change this
+const std::string kHelloMessage = "HELLO IS IT ME YOU'RE LOOKING FOR?";
+
+class NrTcpSocketTest : public MtransportTest {
+ public:
+ NrTcpSocketTest()
+ : mSProxy(nullptr),
+ nr_socket_(nullptr),
+ mEmptyArray(0),
+ mReadChunkSize(0),
+ mReadChunkSizeIncrement(1),
+ mReadAllowance(-1),
+ mConnected(false) {}
+
+ void SetUp() override {
+ mSProxy = new NrTcpSocket(nullptr);
+ int r = nr_socket_create_int((void*)mSProxy.get(), mSProxy->vtbl(),
+ &nr_socket_);
+ ASSERT_EQ(0, r);
+
+ // fake calling AsyncOpen() due to IPC calls. must be non-null
+ mSProxy->AssignChannel_DoNotUse(new WebrtcTCPSocketWrapper(nullptr));
+ }
+
+ void TearDown() override { mSProxy->close(); }
+
+ static void readable_cb(NR_SOCKET s, int how, void* cb_arg) {
+ NrTcpSocketTest* test = (NrTcpSocketTest*)cb_arg;
+ size_t capacity = std::min(test->mReadChunkSize, test->mReadAllowance);
+ nsTArray<uint8_t> array(capacity);
+ size_t read;
+
+ nr_socket_read(test->nr_socket_, (char*)array.Elements(), array.Capacity(),
+ &read, 0);
+
+ ASSERT_TRUE(read <= array.Capacity());
+ ASSERT_TRUE(test->mReadAllowance >= read);
+
+ array.SetLength(read);
+ test->mData.AppendElements(array);
+ test->mReadAllowance -= read;
+
+ // We may read more bytes each time we're called. This way we can ensure we
+ // consume buffers partially and across multiple buffers.
+ test->mReadChunkSize += test->mReadChunkSizeIncrement;
+
+ if (test->mReadAllowance > 0) {
+ NR_ASYNC_WAIT(s, how, &NrTcpSocketTest::readable_cb, cb_arg);
+ }
+ }
+
+ static void writable_cb(NR_SOCKET s, int how, void* cb_arg) {
+ NrTcpSocketTest* test = (NrTcpSocketTest*)cb_arg;
+ test->mConnected = true;
+ }
+
+ const std::string DataString() {
+ return std::string((char*)mData.Elements(), mData.Length());
+ }
+
+ protected:
+ RefPtr<NrTcpSocket> mSProxy;
+ nr_socket* nr_socket_;
+
+ nsTArray<uint8_t> mData;
+ nsTArray<uint8_t> mEmptyArray;
+
+ uint32_t mReadChunkSize;
+ uint32_t mReadChunkSizeIncrement;
+ uint32_t mReadAllowance;
+
+ bool mConnected;
+};
+
+TEST_F(NrTcpSocketTest, TestCreate) {}
+
+TEST_F(NrTcpSocketTest, TestConnected) {
+ ASSERT_TRUE(!mConnected);
+
+ NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_WRITE, &NrTcpSocketTest::writable_cb,
+ this);
+
+ // still not connected just registered for writes...
+ ASSERT_TRUE(!mConnected);
+
+ mSProxy->OnConnected("http"_ns);
+
+ ASSERT_TRUE(mConnected);
+}
+
+TEST_F(NrTcpSocketTest, TestRead) {
+ nsTArray<uint8_t> array;
+ array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
+
+ NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
+ this);
+ // this will read 0 bytes here
+ mSProxy->OnRead(std::move(array));
+
+ ASSERT_EQ(kHelloMessage.length(), mSProxy->CountUnreadBytes());
+
+ // callback is still set but terminated due to 0 byte read
+ // start callbacks again (first read is 0 then 1,2,3,...)
+ mSProxy->OnRead(std::move(mEmptyArray));
+
+ ASSERT_EQ(kHelloMessage.length(), mData.Length());
+ ASSERT_EQ(kHelloMessage, DataString());
+}
+
+TEST_F(NrTcpSocketTest, TestReadConstantConsumeSize) {
+ std::string data;
+
+ // triangle number
+ const int kCount = 32;
+
+ // ~17kb
+ // triangle number formula n*(n+1)/2
+ for (int i = 0; i < kCount * (kCount + 1) / 2; ++i) {
+ data += kHelloMessage;
+ }
+
+ // decreasing buffer sizes
+ for (int i = 0, start = 0; i < kCount; ++i) {
+ int length = (kCount - i) * kHelloMessage.length();
+
+ nsTArray<uint8_t> array;
+ array.AppendElements(data.c_str() + start, length);
+ start += length;
+
+ mSProxy->OnRead(std::move(array));
+ }
+
+ ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes());
+
+ // read same amount each callback
+ mReadChunkSize = 128;
+ mReadChunkSizeIncrement = 0;
+ NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
+ this);
+
+ ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes());
+
+ // start callbacks
+ mSProxy->OnRead(std::move(mEmptyArray));
+
+ ASSERT_EQ(data.length(), mData.Length());
+ ASSERT_EQ(data, DataString());
+}
+
+TEST_F(NrTcpSocketTest, TestReadNone) {
+ char buf[4096];
+ size_t read = 0;
+ int r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+
+ ASSERT_EQ(R_WOULDBLOCK, r);
+
+ nsTArray<uint8_t> array;
+ array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
+ mSProxy->OnRead(std::move(array));
+
+ ASSERT_EQ(kHelloMessage.length(), mSProxy->CountUnreadBytes());
+
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(kHelloMessage.length(), read);
+ ASSERT_EQ(kHelloMessage, std::string(buf, read));
+}
+
+TEST_F(NrTcpSocketTest, TestReadMultipleSizes) {
+ using namespace std;
+
+ string data;
+ // 515 * kHelloMessage.length() == 17510
+ const size_t kCount = 515;
+ // randomly generated numbers, sums to 17510, 20 numbers
+ vector<int> varyingSizes = {404, 622, 1463, 1597, 1676, 389, 389,
+ 1272, 781, 81, 1030, 1450, 256, 812,
+ 1571, 29, 1045, 911, 643, 1089};
+
+ // changing varyingSizes or the test message breaks this so check here
+ ASSERT_EQ(kCount, 17510 / kHelloMessage.length());
+ ASSERT_EQ(17510, accumulate(varyingSizes.begin(), varyingSizes.end(), 0));
+
+ // ~17kb
+ for (size_t i = 0; i < kCount; ++i) {
+ data += kHelloMessage;
+ }
+
+ nsTArray<uint8_t> array;
+ array.AppendElements(data.c_str(), data.length());
+
+ for (int amountToRead : varyingSizes) {
+ nsTArray<uint8_t> buffer;
+ buffer.AppendElements(array.Elements(), amountToRead);
+ array.RemoveElementsAt(0, amountToRead);
+ mSProxy->OnRead(std::move(buffer));
+ }
+
+ ASSERT_EQ(data.length(), mSProxy->CountUnreadBytes());
+
+ // don't need to read 0 on the first read, so start at 1 and keep going
+ mReadChunkSize = 1;
+ NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
+ this);
+ // start callbacks
+ mSProxy->OnRead(std::move(mEmptyArray));
+
+ ASSERT_EQ(data.length(), mData.Length());
+ ASSERT_EQ(data, DataString());
+}
+
+TEST_F(NrTcpSocketTest, TestReadConsumeReadDrain) {
+ std::string data;
+ // ~26kb total; should be even
+ const int kCount = 512;
+
+ // there's some division by 2 here so check that kCount is even
+ ASSERT_EQ(0, kCount % 2);
+
+ for (int i = 0; i < kCount; ++i) {
+ data += kHelloMessage;
+ nsTArray<uint8_t> array;
+ array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
+ mSProxy->OnRead(std::move(array));
+ }
+
+ // read half at first
+ mReadAllowance = kCount / 2 * kHelloMessage.length();
+ // start by reading 1 byte
+ mReadChunkSize = 1;
+ NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
+ this);
+ mSProxy->OnRead(std::move(mEmptyArray));
+
+ ASSERT_EQ(data.length() / 2, mSProxy->CountUnreadBytes());
+ ASSERT_EQ(data.length() / 2, mData.Length());
+
+ // fill read buffer back up
+ for (int i = 0; i < kCount / 2; ++i) {
+ data += kHelloMessage;
+ nsTArray<uint8_t> array;
+ array.AppendElements(kHelloMessage.c_str(), kHelloMessage.length());
+ mSProxy->OnRead(std::move(array));
+ }
+
+ // remove read limit
+ mReadAllowance = -1;
+ // used entire read allowance so we need to setup a new await
+ NR_ASYNC_WAIT(mSProxy, NR_ASYNC_WAIT_READ, &NrTcpSocketTest::readable_cb,
+ this);
+ // start callbacks
+ mSProxy->OnRead(std::move(mEmptyArray));
+
+ ASSERT_EQ(data.length(), mData.Length());
+ ASSERT_EQ(data, DataString());
+}
diff --git a/dom/media/webrtc/transport/test/rlogconnector_unittest.cpp b/dom/media/webrtc/transport/test/rlogconnector_unittest.cpp
new file mode 100644
index 0000000000..93fabae481
--- /dev/null
+++ b/dom/media/webrtc/transport/test/rlogconnector_unittest.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: 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 <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/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp b/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp
new file mode 100644
index 0000000000..c673139867
--- /dev/null
+++ b/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp
@@ -0,0 +1,343 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsNetCID.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.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 {
+
+// Helper used to make sure args are properly copied and/or moved.
+struct CtorDtorState {
+ enum class State { Empty, Copy, Explicit, Move, Moved };
+
+ static const char* ToStr(const State& state) {
+ switch (state) {
+ case State::Empty:
+ return "empty";
+ case State::Copy:
+ return "copy";
+ case State::Explicit:
+ return "explicit";
+ case State::Move:
+ return "move";
+ case State::Moved:
+ return "moved";
+ default:
+ return "unknown";
+ }
+ }
+
+ void DumpState() const { std::cerr << ToStr(state_) << std::endl; }
+
+ CtorDtorState() { DumpState(); }
+
+ explicit CtorDtorState(int* destroyed)
+ : dtor_count_(destroyed), state_(State::Explicit) {
+ DumpState();
+ }
+
+ CtorDtorState(const CtorDtorState& other)
+ : dtor_count_(other.dtor_count_), state_(State::Copy) {
+ DumpState();
+ }
+
+ // Clear the other's dtor counter so it's not counted if moved.
+ CtorDtorState(CtorDtorState&& other)
+ : dtor_count_(std::exchange(other.dtor_count_, nullptr)),
+ state_(State::Move) {
+ other.state_ = State::Moved;
+ DumpState();
+ }
+
+ ~CtorDtorState() {
+ const char* const state = ToStr(state_);
+ std::cerr << "Destructor called with end state: " << state << std::endl;
+
+ if (dtor_count_) {
+ ++*dtor_count_;
+ }
+ }
+
+ int* dtor_count_ = nullptr;
+ State state_ = State::Empty;
+};
+
+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_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, 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);
+ }
+ ASSERT_TRUE(destroyed);
+
+ // Now try with a move.
+ destroyed = false;
+ {
+ RefPtr<Destructor> destructor = new Destructor(&destroyed);
+ target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target_ref,
+ std::move(destructor)),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(destroyed);
+ }
+}
+
+TEST_F(DispatchTest, TestMove) {
+ int destroyed = 0;
+ {
+ CtorDtorState state(&destroyed);
+
+ // Dispatch with:
+ // - moved arg
+ // - by-val capture in function should consume a move
+ // - expect destruction in the function scope
+ target_->Dispatch(WrapRunnableNM([](CtorDtorState s) {}, std::move(state)),
+ NS_DISPATCH_SYNC);
+ ASSERT_EQ(1, destroyed);
+ }
+ // Still shouldn't count when we go out of scope as it was moved.
+ ASSERT_EQ(1, destroyed);
+
+ {
+ CtorDtorState state(&destroyed);
+
+ // Dispatch with:
+ // - copied arg
+ // - by-val capture in function should consume a move
+ // - expect destruction in the function scope and call scope
+ target_->Dispatch(WrapRunnableNM([](CtorDtorState s) {}, state),
+ NS_DISPATCH_SYNC);
+ ASSERT_EQ(2, destroyed);
+ }
+ // Original state should be destroyed
+ ASSERT_EQ(3, destroyed);
+
+ {
+ CtorDtorState state(&destroyed);
+
+ // Dispatch with:
+ // - moved arg
+ // - by-ref in function should accept a moved arg
+ // - expect destruction in the wrapper invocation scope
+ target_->Dispatch(
+ WrapRunnableNM([](const CtorDtorState& s) {}, std::move(state)),
+ NS_DISPATCH_SYNC);
+ ASSERT_EQ(4, destroyed);
+ }
+ // Still shouldn't count when we go out of scope as it was moved.
+ ASSERT_EQ(4, destroyed);
+
+ {
+ CtorDtorState state(&destroyed);
+
+ // Dispatch with:
+ // - moved arg
+ // - r-value function should accept a moved arg
+ // - expect destruction in the wrapper invocation scope
+ target_->Dispatch(
+ WrapRunnableNM([](CtorDtorState&& s) {}, std::move(state)),
+ NS_DISPATCH_SYNC);
+ ASSERT_EQ(5, destroyed);
+ }
+ // Still shouldn't count when we go out of scope as it was moved.
+ ASSERT_EQ(5, destroyed);
+}
+
+TEST_F(DispatchTest, TestUniquePtr) {
+ // Test that holding the class in UniquePtr works
+ int ran = 0;
+ auto cl = MakeUnique<TargetClass>(&ran);
+
+ target_->Dispatch(WrapRunnable(std::move(cl), &TargetClass::m1, 1),
+ NS_DISPATCH_SYNC);
+ ASSERT_EQ(1, ran);
+
+ // Test that UniquePtr works as a param to the runnable
+ int destroyed = 0;
+ {
+ auto state = MakeUnique<CtorDtorState>(&destroyed);
+
+ // Dispatch with:
+ // - moved arg
+ // - Function should move construct from arg
+ // - expect destruction in the wrapper invocation scope
+ target_->Dispatch(
+ WrapRunnableNM([](UniquePtr<CtorDtorState> s) {}, std::move(state)),
+ NS_DISPATCH_SYNC);
+ ASSERT_EQ(1, destroyed);
+ }
+ // Still shouldn't count when we go out of scope as it was moved.
+ ASSERT_EQ(1, destroyed);
+}
+
+} // end of namespace
diff --git a/dom/media/webrtc/transport/test/sctp_unittest.cpp b/dom/media/webrtc/transport/test/sctp_unittest.cpp
new file mode 100644
index 0000000000..55451dfa6b
--- /dev/null
+++ b/dom/media/webrtc/transport/test/sctp_unittest.cpp
@@ -0,0 +1,382 @@
+/* -*- 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 "sigslot.h"
+
+#include "nsITimer.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 nsINamed {
+ public:
+ SendPeriodic(TransportTestPeer* peer, int to_send)
+ : peer_(peer), to_send_(to_send) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ protected:
+ virtual ~SendPeriodic() = default;
+
+ TransportTestPeer* peer_;
+ int to_send_;
+};
+
+NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback, nsINamed)
+
+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_(NS_NewTimer()),
+ 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, loopback_->Init());
+ flow_->PushLayer(loopback_);
+
+ loopback_->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() {
+ disconnect_all();
+ 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(UniquePtr<MediaPacket> packet,
+ const RefPtr<TransportFlow>& flow,
+ TransportLayer* layer) {
+ return layer->SendPacket(*packet);
+ }
+
+ TransportResult SendPacket(const unsigned char* data, size_t len) {
+ UniquePtr<MediaPacket> packet(new MediaPacket);
+ packet->Copy(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,
+ std::move(packet), flow_, loopback_),
+ NS_DISPATCH_NORMAL);
+
+ return 0;
+ }
+
+ void PacketReceived(TransportLayer* layer, MediaPacket& packet) {
+ std::cerr << "Received " << packet.len() << " bytes" << std::endl;
+
+ // Pass the data to SCTP
+
+ usrsctp_conninput(static_cast<void*>(this), packet.data(), packet.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_;
+ // Owns the TransportLayerLoopback, but basically does nothing else.
+ 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;
+}
+
+NS_IMETHODIMP
+SendPeriodic::GetName(nsACString& aName) {
+ aName.AssignLiteral("SendPeriodic");
+ return NS_OK;
+}
+
+class SctpTransportTest : public MtransportTest {
+ public:
+ SctpTransportTest() = default;
+
+ ~SctpTransportTest() = default;
+
+ 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_ = nullptr;
+ TransportTestPeer* p2_ = nullptr;
+};
+
+TEST_F(SctpTransportTest, TestConnect) { ConnectSocket(); }
+
+TEST_F(SctpTransportTest, TestConnectSymmetricalPorts) {
+ ConnectSocket(5002, 5002);
+}
+
+TEST_F(SctpTransportTest, TestTransfer) {
+ ConnectSocket();
+ TestTransfer(50);
+}
+
+} // end namespace
diff --git a/dom/media/webrtc/transport/test/simpletokenbucket_unittest.cpp b/dom/media/webrtc/transport/test/simpletokenbucket_unittest.cpp
new file mode 100644
index 0000000000..66622d795b
--- /dev/null
+++ b/dom/media/webrtc/transport/test/simpletokenbucket_unittest.cpp
@@ -0,0 +1,114 @@
+/* -*- 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"
+
+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/dom/media/webrtc/transport/test/sockettransportservice_unittest.cpp b/dom/media/webrtc/transport/test/sockettransportservice_unittest.cpp
new file mode 100644
index 0000000000..ffa87fe91f
--- /dev/null
+++ b/dom/media/webrtc/transport/test/sockettransportservice_unittest.cpp
@@ -0,0 +1,181 @@
+/* -*- 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 "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)
+ : Runnable("EventReceived"), 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)
+ : Runnable("RegisterEvent"), 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() = default;
+
+ 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/dom/media/webrtc/transport/test/stunserver.cpp b/dom/media/webrtc/transport/test/stunserver.cpp
new file mode 100644
index 0000000000..b5fce21e19
--- /dev/null
+++ b/dom/media/webrtc/transport/test/stunserver.cpp
@@ -0,0 +1,652 @@
+/* -*- 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 "mediapacket.h"
+
+// mozilla/utils.h defines this as well
+#ifdef UNIMPLEMENTED
+# undef UNIMPLEMENTED
+#endif
+
+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 "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 = nullptr;
+
+ delete wrapped;
+
+ return 0;
+}
+
+static int nr_socket_wrapped_sendto(void* obj, const void* msg, size_t len,
+ int flags, const 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,
+ nullptr,
+ nullptr,
+ nullptr,
+ nr_socket_wrapped_close,
+ nullptr,
+ nullptr};
+
+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;
+}
+
+static int addressFamilyToIpVersion(int address_family) {
+ switch (address_family) {
+ case AF_INET:
+ return NR_IPV4;
+ case AF_INET6:
+ return NR_IPV6;
+ default:
+ MOZ_CRASH();
+ }
+ return NR_IPV4;
+}
+
+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;
+ }
+
+ // removes duplicates and, based on prefs, loopback and link_local addrs
+ r = nr_stun_filter_local_addresses(addrs, &addr_ct);
+ if (r) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't filter 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.ip_version == addressFamilyToIpVersion(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"),
+ &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_(), sock_(sock) {
+ buffer_.Copy(reinterpret_cast<const uint8_t*>(data), len);
+ nr_transport_addr_copy(&addr_, addr);
+ }
+
+ TestStunServer* server_;
+ MediaPacket 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++);
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/test/stunserver.h b/dom/media/webrtc/transport/test/stunserver.h
new file mode 100644
index 0000000000..4903cb89ad
--- /dev/null
+++ b/dom/media/webrtc/transport/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 "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() = default;
+ 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/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp
new file mode 100644
index 0000000000..a411168194
--- /dev/null
+++ b/dom/media/webrtc/transport/test/test_nr_socket_ice_unittest.cpp
@@ -0,0 +1,415 @@
+/* -*- 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 taken from nricectx.cpp and nricemediastream.cpp
+// which in turn contains code 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 "gtest/gtest.h"
+#include "gtest_utils.h"
+
+extern "C" {
+#include "ice_ctx.h"
+#include "ice_peer_ctx.h"
+#include "nICEr/src/net/transport_addr.h"
+}
+
+#include "mtransport_test_utils.h"
+#include "nricectx.h"
+#include "nricemediastream.h"
+#include "runnable_utils.h"
+#include "test_nr_socket.h"
+
+namespace mozilla {
+
+static unsigned int kDefaultTimeout = 7000;
+
+class IcePeer {
+ public:
+ IcePeer(const char* name, TestNat* nat, UINT4 flags,
+ MtransportTestUtils* test_utils)
+ : name_(name),
+ ice_checking_(false),
+ ice_connected_(false),
+ ice_disconnected_(false),
+ gather_cb_(false),
+ stream_ready_(false),
+ stream_failed_(false),
+ ice_ctx_(nullptr),
+ peer_ctx_(nullptr),
+ nat_(nat),
+ test_utils_(test_utils) {
+ nr_ice_ctx_create(const_cast<char*>(name_.c_str()), flags, &ice_ctx_);
+
+ if (nat_) {
+ nr_socket_factory* factory;
+ nat_->create_socket_factory(&factory);
+ nr_ice_ctx_set_socket_factory(ice_ctx_, factory);
+ }
+
+ // Create the handler objects
+ ice_handler_vtbl_ = new nr_ice_handler_vtbl();
+ ice_handler_vtbl_->select_pair = &IcePeer::select_pair;
+ ice_handler_vtbl_->stream_ready = &IcePeer::stream_ready;
+ ice_handler_vtbl_->stream_failed = &IcePeer::stream_failed;
+ ice_handler_vtbl_->ice_connected = &IcePeer::ice_connected;
+ ice_handler_vtbl_->msg_recvd = &IcePeer::msg_recvd;
+ ice_handler_vtbl_->ice_checking = &IcePeer::ice_checking;
+ ice_handler_vtbl_->ice_disconnected = &IcePeer::ice_disconnected;
+
+ ice_handler_ = new nr_ice_handler();
+ ice_handler_->vtbl = ice_handler_vtbl_;
+ ice_handler_->obj = this;
+
+ nr_ice_peer_ctx_create(ice_ctx_, ice_handler_,
+ const_cast<char*>(name_.c_str()), &peer_ctx_);
+
+ nr_ice_add_media_stream(ice_ctx_, const_cast<char*>(name_.c_str()), "ufrag",
+ "pass", 2, &ice_media_stream_);
+ EXPECT_EQ(2UL, GetStreamAttributes().size());
+
+ nr_ice_media_stream_initialize(ice_ctx_, ice_media_stream_);
+ }
+
+ virtual ~IcePeer() { Destroy(); }
+
+ void Destroy() {
+ test_utils_->sts_target()->Dispatch(WrapRunnable(this, &IcePeer::Destroy_s),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Destroy_s() {
+ nr_ice_peer_ctx_destroy(&peer_ctx_);
+ delete ice_handler_;
+ delete ice_handler_vtbl_;
+ nr_ice_ctx_destroy(&ice_ctx_);
+ }
+
+ void Gather(bool default_route_only = false) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IcePeer::Gather_s, default_route_only),
+ NS_DISPATCH_SYNC);
+ }
+
+ void Gather_s(bool default_route_only = false) {
+ int r = nr_ice_gather(ice_ctx_, &IcePeer::gather_cb, this);
+ ASSERT_TRUE(r == 0 || r == R_WOULDBLOCK);
+ }
+
+ std::vector<std::string> GetStreamAttributes() {
+ std::vector<std::string> attributes;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&attributes, this, &IcePeer::GetStreamAttributes_s),
+ NS_DISPATCH_SYNC);
+ return attributes;
+ }
+
+ std::vector<std::string> GetStreamAttributes_s() {
+ char** attrs = nullptr;
+ int attrct;
+ std::vector<std::string> ret;
+
+ int r =
+ nr_ice_media_stream_get_attributes(ice_media_stream_, &attrs, &attrct);
+ EXPECT_EQ(0, r);
+
+ for (int i = 0; i < attrct; i++) {
+ ret.push_back(std::string(attrs[i]));
+ RFREE(attrs[i]);
+ }
+ RFREE(attrs);
+
+ return ret;
+ }
+
+ std::vector<std::string> GetGlobalAttributes() {
+ std::vector<std::string> attributes;
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&attributes, this, &IcePeer::GetGlobalAttributes_s),
+ NS_DISPATCH_SYNC);
+ return attributes;
+ }
+
+ std::vector<std::string> GetGlobalAttributes_s() {
+ char** attrs = nullptr;
+ int attrct;
+ std::vector<std::string> ret;
+
+ nr_ice_get_global_attributes(ice_ctx_, &attrs, &attrct);
+
+ for (int i = 0; i < attrct; i++) {
+ ret.push_back(std::string(attrs[i]));
+ RFREE(attrs[i]);
+ }
+ RFREE(attrs);
+
+ return ret;
+ }
+
+ void ParseGlobalAttributes(std::vector<std::string> attrs) {
+ std::vector<char*> attrs_in;
+ attrs_in.reserve(attrs.size());
+ for (auto& attr : attrs) {
+ attrs_in.push_back(const_cast<char*>(attr.c_str()));
+ }
+
+ int r = nr_ice_peer_ctx_parse_global_attributes(
+ peer_ctx_, attrs_in.empty() ? nullptr : &attrs_in[0], attrs_in.size());
+ ASSERT_EQ(0, r);
+ }
+
+ void SetControlling(bool controlling) {
+ peer_ctx_->controlling = controlling ? 1 : 0;
+ }
+
+ void SetRemoteAttributes(std::vector<std::string> attributes) {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IcePeer::SetRemoteAttributes_s, attributes),
+ NS_DISPATCH_SYNC);
+ }
+
+ void SetRemoteAttributes_s(std::vector<std::string> attributes) {
+ int r;
+
+ std::vector<char*> attrs;
+ attrs.reserve(attributes.size());
+ for (auto& attr : attributes) {
+ attrs.push_back(const_cast<char*>(attr.c_str()));
+ }
+
+ if (!attrs.empty()) {
+ r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_,
+ &attrs[0], attrs.size());
+ ASSERT_EQ(0, r);
+ }
+ }
+
+ void StartChecks() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IcePeer::StartChecks_s), NS_DISPATCH_SYNC);
+ }
+
+ void StartChecks_s() {
+ int r = nr_ice_peer_ctx_pair_candidates(peer_ctx_);
+ ASSERT_EQ(0, r);
+
+ r = nr_ice_peer_ctx_start_checks2(peer_ctx_, 1);
+ ASSERT_EQ(0, r);
+ }
+
+ // Handler callbacks
+ static int select_pair(void* obj, nr_ice_media_stream* stream,
+ int component_id, nr_ice_cand_pair** potentials,
+ int potential_ct) {
+ return 0;
+ }
+
+ static int stream_ready(void* obj, nr_ice_media_stream* stream) {
+ IcePeer* peer = static_cast<IcePeer*>(obj);
+ peer->stream_ready_ = true;
+ return 0;
+ }
+
+ static int stream_failed(void* obj, nr_ice_media_stream* stream) {
+ IcePeer* peer = static_cast<IcePeer*>(obj);
+ peer->stream_failed_ = true;
+ return 0;
+ }
+
+ static int ice_checking(void* obj, nr_ice_peer_ctx* pctx) {
+ IcePeer* peer = static_cast<IcePeer*>(obj);
+ peer->ice_checking_ = true;
+ return 0;
+ }
+
+ static int ice_connected(void* obj, nr_ice_peer_ctx* pctx) {
+ IcePeer* peer = static_cast<IcePeer*>(obj);
+ peer->ice_connected_ = true;
+ return 0;
+ }
+
+ static int ice_disconnected(void* obj, nr_ice_peer_ctx* pctx) {
+ IcePeer* peer = static_cast<IcePeer*>(obj);
+ peer->ice_disconnected_ = true;
+ return 0;
+ }
+
+ static int msg_recvd(void* obj, nr_ice_peer_ctx* pctx,
+ nr_ice_media_stream* stream, int component_id,
+ UCHAR* msg, int len) {
+ return 0;
+ }
+
+ static void gather_cb(NR_SOCKET s, int h, void* arg) {
+ IcePeer* peer = static_cast<IcePeer*>(arg);
+ peer->gather_cb_ = true;
+ }
+
+ std::string name_;
+
+ bool ice_checking_;
+ bool ice_connected_;
+ bool ice_disconnected_;
+ bool gather_cb_;
+ bool stream_ready_;
+ bool stream_failed_;
+
+ nr_ice_ctx* ice_ctx_;
+ nr_ice_handler* ice_handler_;
+ nr_ice_handler_vtbl* ice_handler_vtbl_;
+ nr_ice_media_stream* ice_media_stream_;
+ nr_ice_peer_ctx* peer_ctx_;
+ TestNat* nat_;
+ MtransportTestUtils* test_utils_;
+};
+
+class TestNrSocketIceUnitTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ NSS_NoDB_Init(nullptr);
+ NSS_SetDomesticPolicy();
+
+ test_utils_ = new MtransportTestUtils();
+ test_utils2_ = new MtransportTestUtils();
+
+ NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
+ }
+
+ void TearDown() override {
+ delete test_utils_;
+ delete test_utils2_;
+ }
+
+ MtransportTestUtils* test_utils_;
+ MtransportTestUtils* test_utils2_;
+};
+
+TEST_F(TestNrSocketIceUnitTest, TestIcePeer) {
+ IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
+ test_utils_);
+ ASSERT_NE(peer.ice_ctx_, nullptr);
+ ASSERT_NE(peer.peer_ctx_, nullptr);
+ ASSERT_NE(peer.ice_media_stream_, nullptr);
+ ASSERT_EQ(2UL, peer.GetStreamAttributes().size())
+ << "Should have ice-ufrag and ice-pwd";
+ peer.Gather();
+ ASSERT_LT(2UL, peer.GetStreamAttributes().size())
+ << "Should have ice-ufrag, ice-pwd, and at least one candidate.";
+}
+
+TEST_F(TestNrSocketIceUnitTest, TestIcePeersNoNAT) {
+ IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
+ test_utils_);
+ IcePeer peer2("IcePeer2", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
+ test_utils2_);
+ peer.SetControlling(true);
+ peer2.SetControlling(false);
+
+ peer.Gather();
+ peer2.Gather();
+ std::vector<std::string> attrs = peer.GetGlobalAttributes();
+ peer2.ParseGlobalAttributes(attrs);
+ std::vector<std::string> attributes = peer.GetStreamAttributes();
+ peer2.SetRemoteAttributes(attributes);
+
+ attrs = peer2.GetGlobalAttributes();
+ peer.ParseGlobalAttributes(attrs);
+ attributes = peer2.GetStreamAttributes();
+ peer.SetRemoteAttributes(attributes);
+ peer2.StartChecks();
+ peer.StartChecks();
+
+ ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
+ ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
+}
+
+TEST_F(TestNrSocketIceUnitTest, TestIcePeersPacketLoss) {
+ IcePeer peer("IcePeer", nullptr, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
+ test_utils_);
+
+ RefPtr<TestNat> nat(new TestNat);
+ class NatDelegate : public TestNat::NatDelegate {
+ public:
+ NatDelegate() : messages(0) {}
+
+ int on_read(TestNat* nat, void* buf, size_t maxlen, size_t* len) override {
+ return 0;
+ }
+
+ int on_sendto(TestNat* nat, const void* msg, size_t len, int flags,
+ const nr_transport_addr* to) override {
+ ++messages;
+ // 25% packet loss
+ if (messages % 4 == 0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ int on_write(TestNat* nat, const void* msg, size_t len,
+ size_t* written) override {
+ return 0;
+ }
+
+ int messages;
+ } delegate;
+ nat->nat_delegate_ = &delegate;
+
+ IcePeer peer2("IcePeer2", nat, NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION,
+ test_utils2_);
+ peer.SetControlling(true);
+ peer2.SetControlling(false);
+
+ peer.Gather();
+ peer2.Gather();
+ std::vector<std::string> attrs = peer.GetGlobalAttributes();
+ peer2.ParseGlobalAttributes(attrs);
+ std::vector<std::string> attributes = peer.GetStreamAttributes();
+ peer2.SetRemoteAttributes(attributes);
+
+ attrs = peer2.GetGlobalAttributes();
+ peer.ParseGlobalAttributes(attrs);
+ attributes = peer2.GetStreamAttributes();
+ peer.SetRemoteAttributes(attributes);
+ peer2.StartChecks();
+ peer.StartChecks();
+
+ ASSERT_TRUE_WAIT(peer.ice_connected_, kDefaultTimeout);
+ ASSERT_TRUE_WAIT(peer2.ice_connected_, kDefaultTimeout);
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp b/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp
new file mode 100644
index 0000000000..dcc5001b17
--- /dev/null
+++ b/dom/media/webrtc/transport/test/test_nr_socket_unittest.cpp
@@ -0,0 +1,808 @@
+/* -*- 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 <cstddef>
+
+extern "C" {
+#include "r_errors.h"
+#include "async_wait.h"
+}
+
+#include "test_nr_socket.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.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, &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::NrSocketBase;
+using mozilla::TestNat;
+using mozilla::TestNrSocketTest;
+
+TEST_F(TestNrSocketTest, UnsafePortRejectedUDP) {
+ nr_transport_addr address;
+ ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1",
+ // ssh
+ 22, IPPROTO_UDP, &address));
+ ASSERT_TRUE(NrSocketBase::IsForbiddenAddress(&address));
+}
+
+TEST_F(TestNrSocketTest, UnsafePortRejectedTCP) {
+ nr_transport_addr address;
+ ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1",
+ // ssh
+ 22, IPPROTO_TCP, &address));
+ ASSERT_TRUE(NrSocketBase::IsForbiddenAddress(&address));
+}
+
+TEST_F(TestNrSocketTest, SafePortAcceptedUDP) {
+ nr_transport_addr address;
+ ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1",
+ // stuns
+ 5349, IPPROTO_UDP, &address));
+ ASSERT_FALSE(NrSocketBase::IsForbiddenAddress(&address));
+}
+
+TEST_F(TestNrSocketTest, SafePortAcceptedTCP) {
+ nr_transport_addr address;
+ ASSERT_FALSE(nr_str_port_to_transport_addr("127.0.0.1",
+ // turns
+ 5349, IPPROTO_TCP, &address));
+ ASSERT_FALSE(NrSocketBase::IsForbiddenAddress(&address));
+}
+
+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/dom/media/webrtc/transport/test/transport_unittests.cpp b/dom/media/webrtc/transport/test/transport_unittests.cpp
new file mode 100644
index 0000000000..b88450486a
--- /dev/null
+++ b/dom/media/webrtc/transport/test/transport_unittests.cpp
@@ -0,0 +1,1416 @@
+
+/* 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 <algorithm>
+#include <functional>
+
+#ifdef XP_MACOSX
+// ensure that Apple Security kit enum goes before "sslproto.h"
+# include <CoreFoundation/CFAvailability.h>
+# include <Security/CipherSuite.h>
+#endif
+
+#include "mozilla/UniquePtr.h"
+
+#include "sigslot.h"
+
+#include "logging.h"
+#include "ssl.h"
+#include "sslexp.h"
+#include "sslproto.h"
+
+#include "nsThreadUtils.h"
+
+#include "mediapacket.h"
+#include "dtlsidentity.h"
+#include "nricectx.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; }
+
+ nsresult InitInternal() override {
+ return allow_init_ ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ TransportResult SendPacket(MediaPacket& packet) override {
+ MOZ_CRASH(); // Should never be called.
+ return 0;
+ }
+
+ TRANSPORT_LAYER_ID("lossy")
+
+ private:
+ bool allow_init_;
+ bool* destroyed_;
+};
+
+class Inspector {
+ public:
+ virtual ~Inspector() = default;
+
+ 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() = default;
+
+ TransportResult SendPacket(MediaPacket& packet) override {
+ MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "SendPacket(" << packet.len() << ")");
+
+ if (loss_mask_ & (1 << (packet_ % 32))) {
+ MOZ_MTLOG(ML_NOTICE, "Dropping packet");
+ ++packet_;
+ return packet.len();
+ }
+ if (inspector_) {
+ inspector_->Inspect(this, packet.data(), packet.len());
+ }
+
+ ++packet_;
+
+ return downward_->SendPacket(packet);
+ }
+
+ void SetLoss(uint32_t packet) { loss_mask_ |= (1 << (packet & 32)); }
+
+ void SetInspector(UniquePtr<Inspector> inspector) {
+ inspector_ = std::move(inspector);
+ }
+
+ void StateChange(TransportLayer* layer, State state) { TL_SET_STATE(state); }
+
+ void PacketReceived(TransportLayer* layer, MediaPacket& packet) {
+ SignalPacketReceived(this, packet);
+ }
+
+ TRANSPORT_LAYER_ID("lossy")
+
+ protected:
+ void WasInserted() override {
+ 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_(), offset_(0) {
+ buffer_.Copy(data, len);
+ }
+
+ 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; }
+
+ MediaPacket buffer_;
+ size_t offset_;
+};
+
+class DtlsRecordParser {
+ public:
+ DtlsRecordParser(const unsigned char* data, size_t len)
+ : buffer_(), offset_(0) {
+ buffer_.Copy(data, len);
+ }
+
+ bool NextRecord(uint8_t* ct, UniquePtr<MediaPacket>* 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);
+ auto db = MakeUnique<MediaPacket>();
+ db->Copy(ptr(), length);
+ consume(length);
+
+ *ct = *ctp;
+ *buffer = std::move(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; }
+
+ MediaPacket 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;
+ UniquePtr<MediaPacket> 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) {
+ packet_.Copy(data, len);
+ }
+
+ virtual void OnRecord(TransportLayer* layer, uint8_t content_type,
+ const unsigned char* data, size_t len) {
+ // Only inject once.
+ if (!packet_.data()) {
+ 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(packet_);
+ packet_.Reset();
+ }
+
+ private:
+ uint8_t packet_type_;
+ uint8_t handshake_type_;
+ MediaPacket packet_;
+};
+
+// 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;
+ }
+
+ UniquePtr<uint8_t[]> buffer(new uint8_t[length]);
+ if (!parser.Read(buffer.get(), length)) {
+ return;
+ }
+ buffer_.Take(std::move(buffer), length);
+ }
+
+ const MediaPacket& buffer() { return buffer_; }
+
+ private:
+ uint8_t handshake_type_;
+ MediaPacket 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;
+ }
+
+ UniquePtr<uint8_t[]> key(new uint8_t[point_length]);
+ if (!parser.Read(key.get(), point_length)) {
+ return false;
+ }
+ public_key_.Take(std::move(key), point_length);
+
+ return true;
+ }
+
+ MediaPacket public_key_;
+};
+
+namespace {
+class TransportTestPeer : public sigslot::has_slots<> {
+ public:
+ TransportTestPeer(nsCOMPtr<nsIEventTarget> target, std::string name,
+ MtransportTestUtils* utils)
+ : name_(name),
+ offerer_(name == "P1"),
+ 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_(),
+ streams_(),
+ peer_(nullptr),
+ gathering_complete_(false),
+ digest_("sha-1"),
+ enabled_cipersuites_(),
+ disabled_cipersuites_(),
+ test_utils_(utils) {
+ NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
+ ice_ctx_ = NrIceCtx::Create(name);
+ 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_->SetStunServers(stun_servers)));
+
+ dtls_->SetIdentity(identity_);
+ dtls_->SetRole(offerer_ ? TransportLayerDtls::SERVER
+ : TransportLayerDtls::CLIENT);
+
+ nsresult res = identity_->ComputeFingerprint(&digest_);
+ EXPECT_TRUE(NS_SUCCEEDED(res));
+ EXPECT_EQ(20u, digest_.value_.size());
+ }
+
+ ~TransportTestPeer() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &TransportTestPeer::DestroyFlow), NS_DISPATCH_SYNC);
+ }
+
+ void DestroyFlow() {
+ disconnect_all();
+ if (flow_) {
+ loopback_->Disconnect();
+ flow_ = nullptr;
+ }
+ ice_ctx_->Destroy();
+ ice_ctx_ = nullptr;
+ streams_.clear();
+ }
+
+ void DisconnectDestroyFlow() {
+ test_utils_->sts_target()->Dispatch(
+ NS_NewRunnableFunction(
+ __func__,
+ [this] {
+ loopback_->Disconnect();
+ disconnect_all(); // Disconnect from the signals;
+ flow_ = nullptr;
+ }),
+ NS_DISPATCH_SYNC);
+ }
+
+ 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++) {
+ DtlsDigest digest_to_set(peer->digest_);
+
+ if (damage & mask) digest_to_set.value_.data()[0]++;
+
+ nsresult res = dtls_->SetVerificationDigest(digest_to_set);
+
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+
+ mask <<= 1;
+ }
+ }
+
+ void SetupSrtp() {
+ std::vector<uint16_t> srtp_ciphers =
+ TransportLayerDtls::GetDefaultSrtpCiphers();
+ 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, loopback_->Init());
+ ASSERT_EQ((nsresult)NS_OK, logging_->Init());
+ ASSERT_EQ((nsresult)NS_OK, lossy_->Init());
+ ASSERT_EQ((nsresult)NS_OK, dtls_->Init());
+ dtls_->Chain(lossy_);
+ lossy_->Chain(logging_);
+ logging_->Chain(loopback_);
+
+ flow_->PushLayer(loopback_);
+ flow_->PushLayer(logging_);
+ flow_->PushLayer(lossy_);
+ flow_->PushLayer(dtls_);
+
+ if (dtls_->state() != TransportLayer::TS_ERROR) {
+ // Don't execute these blocks if DTLS didn't initialize.
+ TweakCiphers(dtls_->internal_fd());
+ if (post_setup_) {
+ post_setup_(dtls_->internal_fd());
+ }
+ }
+
+ dtls_->SignalPacketReceived.connect(this,
+ &TransportTestPeer::PacketReceived);
+ }
+
+ void TweakCiphers(PRFileDesc* fd) {
+ for (unsigned short& enabled_cipersuite : enabled_cipersuites_) {
+ SSL_CipherPrefSet(fd, enabled_cipersuite, PR_TRUE);
+ }
+ for (unsigned short& disabled_cipersuite : disabled_cipersuites_) {
+ SSL_CipherPrefSet(fd, disabled_cipersuite, PR_FALSE);
+ }
+ }
+
+ void ConnectSocket(TransportTestPeer* peer) {
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnable(this, &TransportTestPeer::ConnectSocket_s, peer),
+ NS_DISPATCH_SYNC);
+ }
+
+ nsresult InitIce_s() {
+ nsresult rv = ice_->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = dtls_->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+ dtls_->Chain(ice_);
+ flow_->PushLayer(ice_);
+ flow_->PushLayer(dtls_);
+ return NS_OK;
+ }
+
+ void InitIce() {
+ nsresult res;
+
+ // Attach our slots
+ ice_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(name, name, 1);
+
+ ASSERT_TRUE(stream != nullptr);
+ stream->SetIceCredentials("ufrag", "pass");
+ streams_.push_back(stream);
+
+ // Listen for candidates
+ stream->SignalCandidate.connect(this, &TransportTestPeer::GotCandidate);
+
+ // Create the transport layer
+ ice_ = new TransportLayerIce();
+ ice_->SetParameters(stream, 1);
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, this, &TransportTestPeer::InitIce_s),
+ NS_DISPATCH_SYNC);
+
+ ASSERT_EQ((nsresult)NS_OK, res);
+
+ // Listen for media events
+ dtls_->SignalPacketReceived.connect(this,
+ &TransportTestPeer::PacketReceived);
+ dtls_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
+
+ // Start gathering
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, ice_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,
+ const std::string& ufrag, const std::string& mdns_addr,
+ const std::string& actual_addr) {
+ std::cerr << "Got candidate " << candidate << " (ufrag=" << ufrag << ")"
+ << std::endl;
+ }
+
+ 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_, &NrIceCtx::ParseGlobalAttributes,
+ ice_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::ConnectToPeer, "ufrag", "pass",
+ streams_[i]->GetAttributes()),
+ NS_DISPATCH_SYNC);
+
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ // Start checks on the other peer.
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableRet(&res, peer_->ice_ctx_, &NrIceCtx::StartChecks),
+ NS_DISPATCH_SYNC);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ }
+
+ // WrapRunnable/lambda and move semantics (MediaPacket is not copyable) don't
+ // get along yet, so we need a wrapper. Gross.
+ static TransportResult SendPacketWrapper(TransportLayer* layer,
+ MediaPacket* packet) {
+ return layer->SendPacket(*packet);
+ }
+
+ TransportResult SendPacket(MediaPacket& packet) {
+ TransportResult ret;
+
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnableNMRet(&ret, &TransportTestPeer::SendPacketWrapper, dtls_,
+ &packet),
+ NS_DISPATCH_SYNC);
+
+ return ret;
+ }
+
+ void StateChanged(TransportLayer* layer, TransportLayer::State state) {
+ if (state == TransportLayer::TS_OPEN) {
+ std::cerr << "Now connected" << std::endl;
+ }
+ }
+
+ void PacketReceived(TransportLayer* layer, MediaPacket& packet) {
+ std::cerr << "Received " << packet.len() << " bytes" << std::endl;
+ ++received_packets_;
+ received_bytes_ += packet.len();
+ }
+
+ void SetLoss(uint32_t loss) { lossy_->SetLoss(loss); }
+
+ void SetCombinePackets(bool combine) { loopback_->CombinePackets(combine); }
+
+ void SetInspector(UniquePtr<Inspector> inspector) {
+ lossy_->SetInspector(std::move(inspector));
+ }
+
+ void SetInspector(Inspector* in) {
+ UniquePtr<Inspector> inspector(in);
+
+ lossy_->SetInspector(std::move(inspector));
+ }
+
+ void SetCipherSuiteChanges(const std::vector<uint16_t>& enableThese,
+ const std::vector<uint16_t>& disableThese) {
+ disabled_cipersuites_ = disableThese;
+ enabled_cipersuites_ = enableThese;
+ }
+
+ void SetPostSetup(const std::function<void(PRFileDesc*)>& setup) {
+ post_setup_ = std::move(setup);
+ }
+
+ TransportLayer::State state() {
+ TransportLayer::State tstate;
+
+ RUN_ON_THREAD(test_utils_->sts_target(),
+ WrapRunnableRet(&tstate, dtls_, &TransportLayer::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_;
+ bool offerer_;
+ 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<NrIceCtx> ice_ctx_;
+ std::vector<RefPtr<NrIceMediaStream> > streams_;
+ TransportTestPeer* peer_;
+ bool gathering_complete_;
+ DtlsDigest digest_;
+ std::vector<uint16_t> enabled_cipersuites_;
+ std::vector<uint16_t> disabled_cipersuites_;
+ MtransportTestUtils* test_utils_;
+ std::function<void(PRFileDesc* fd)> post_setup_ = nullptr;
+};
+
+class TransportTest : public MtransportTest {
+ public:
+ TransportTest() {
+ fds_[0] = nullptr;
+ fds_[1] = nullptr;
+ p1_ = nullptr;
+ p2_ = 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() {
+ if (p1_) {
+ delete p1_;
+ }
+ if (p2_) {
+ delete p2_;
+ }
+ 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 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));
+ MediaPacket packet;
+ packet.Copy(buf, sizeof(buf));
+ TransportResult rv = p1_->SendPacket(packet);
+ 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 with default value
+ ASSERT_EQ(kDtlsSrtpAeadAes128Gcm, 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) {
+ auto set_reuse_ecdhe_key = [](PRFileDesc* fd) {
+ // 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(fd, SSL_REUSE_SERVER_ECDHE_KEY, PR_TRUE);
+ ASSERT_EQ(SECSuccess, rv);
+ };
+
+ SetDtlsPeer();
+ DtlsInspectorRecordHandshakeMessage* i1 =
+ new DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+ p1_->SetInspector(i1);
+ p1_->SetPostSetup(set_reuse_ecdhe_key);
+ ConnectSocket();
+ TlsServerKeyExchangeECDHE dhe1;
+ ASSERT_TRUE(dhe1.Parse(i1->buffer().data(), i1->buffer().len()));
+
+ Reset();
+ SetDtlsPeer();
+ DtlsInspectorRecordHandshakeMessage* i2 =
+ new DtlsInspectorRecordHandshakeMessage(kTlsHandshakeServerKeyExchange);
+
+ p1_->SetInspector(i2);
+ p1_->SetPostSetup(set_reuse_ecdhe_key);
+
+ 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(kDtlsSrtpAes128CmHmacSha1_80);
+ std::vector<uint16_t> setB;
+ setB.push_back(kDtlsSrtpAes128CmHmacSha1_32);
+
+ p1_->SetSrtpCiphers(setA);
+ p2_->SetSrtpCiphers(setB);
+ SetDtlsPeer();
+ ConnectSocketExpectFail();
+
+ ASSERT_EQ(0, p1_->srtpCipher());
+ ASSERT_EQ(0, p2_->srtpCipher());
+}
+
+static SECStatus NoopXtnHandler(PRFileDesc* fd, SSLHandshakeType message,
+ const uint8_t* data, unsigned int len,
+ SSLAlertDescription* alert, void* arg) {
+ return SECSuccess;
+}
+
+static PRBool WriteFixedXtn(PRFileDesc* fd, SSLHandshakeType message,
+ uint8_t* data, unsigned int* len,
+ unsigned int max_len, void* arg) {
+ // When we enable TLS 1.3, change ssl_hs_server_hello here to
+ // ssl_hs_encrypted_extensions. At the same time, add a test that writes to
+ // ssl_hs_server_hello, which should fail.
+ if (message != ssl_hs_client_hello && message != ssl_hs_server_hello) {
+ return false;
+ }
+
+ auto v = reinterpret_cast<std::vector<uint8_t>*>(arg);
+ memcpy(data, &((*v)[0]), v->size());
+ *len = v->size();
+ return true;
+}
+
+// Note that |value| needs to be readable after this function returns.
+static void InstallBadSrtpExtensionWriter(TransportTestPeer* peer,
+ std::vector<uint8_t>* value) {
+ peer->SetPostSetup([value](PRFileDesc* fd) {
+ // Override the handler that is installed by the DTLS setup.
+ SECStatus rv = SSL_InstallExtensionHooks(
+ fd, ssl_use_srtp_xtn, WriteFixedXtn, value, NoopXtnHandler, nullptr);
+ ASSERT_EQ(SECSuccess, rv);
+ });
+}
+
+TEST_F(TransportTest, TestSrtpErrorServerSendsTwoSrtpCiphers) {
+ // Server (p1_) sends an extension with two values, and empty MKI.
+ std::vector<uint8_t> xtn = {0x04, 0x00, 0x01, 0x00, 0x02, 0x00};
+ InstallBadSrtpExtensionWriter(p1_, &xtn);
+ SetupSrtp();
+ SetDtlsPeer();
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestSrtpErrorServerSendsTwoMki) {
+ // Server (p1_) sends an MKI.
+ std::vector<uint8_t> xtn = {0x02, 0x00, 0x01, 0x01, 0x00};
+ InstallBadSrtpExtensionWriter(p1_, &xtn);
+ SetupSrtp();
+ SetDtlsPeer();
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestSrtpErrorServerSendsUnknownValue) {
+ std::vector<uint8_t> xtn = {0x02, 0x9a, 0xf1, 0x00};
+ InstallBadSrtpExtensionWriter(p1_, &xtn);
+ SetupSrtp();
+ SetDtlsPeer();
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestSrtpErrorServerSendsOverflow) {
+ std::vector<uint8_t> xtn = {0x32, 0x00, 0x01, 0x00};
+ InstallBadSrtpExtensionWriter(p1_, &xtn);
+ SetupSrtp();
+ SetDtlsPeer();
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestSrtpErrorServerSendsUnevenList) {
+ std::vector<uint8_t> xtn = {0x01, 0x00, 0x00};
+ InstallBadSrtpExtensionWriter(p1_, &xtn);
+ SetupSrtp();
+ SetDtlsPeer();
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, TestSrtpErrorClientSendsUnevenList) {
+ std::vector<uint8_t> xtn = {0x01, 0x00, 0x00};
+ InstallBadSrtpExtensionWriter(p2_, &xtn);
+ SetupSrtp();
+ SetDtlsPeer();
+ ConnectSocketExpectFail();
+}
+
+TEST_F(TransportTest, OnlyServerSendsSrtpXtn) {
+ p1_->SetupSrtp();
+ SetDtlsPeer();
+ // This should connect, but with no SRTP extension neogtiated.
+ // The client side might negotiate a data channel only.
+ ConnectSocket();
+ ASSERT_NE(TLS_NULL_WITH_NULL_NULL, p1_->cipherSuite());
+ ASSERT_EQ(0, p1_->srtpCipher());
+}
+
+TEST_F(TransportTest, OnlyClientSendsSrtpXtn) {
+ p2_->SetupSrtp();
+ SetDtlsPeer();
+ // This should connect, but with no SRTP extension neogtiated.
+ // The server side might negotiate a data channel only.
+ ConnectSocket();
+ ASSERT_NE(TLS_NULL_WITH_NULL_NULL, p1_->cipherSuite());
+ ASSERT_EQ(0, p1_->srtpCipher());
+}
+
+class TransportSrtpParameterTest
+ : public TransportTest,
+ public ::testing::WithParamInterface<uint16_t> {};
+
+INSTANTIATE_TEST_SUITE_P(
+ SrtpParamInit, TransportSrtpParameterTest,
+ ::testing::ValuesIn(TransportLayerDtls::GetDefaultSrtpCiphers()));
+
+TEST_P(TransportSrtpParameterTest, TestSrtpCiphersMismatchCombinations) {
+ uint16_t cipher = GetParam();
+ std::cerr << "Checking cipher: " << cipher << std::endl;
+
+ p1_->SetupSrtp();
+
+ std::vector<uint16_t> setB;
+ setB.push_back(cipher);
+
+ p2_->SetSrtpCiphers(setB);
+ SetDtlsPeer();
+ ConnectSocket();
+
+ ASSERT_EQ(cipher, p1_->srtpCipher());
+ ASSERT_EQ(cipher, 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();
+}
+
+} // end namespace
diff --git a/dom/media/webrtc/transport/test/turn_unittest.cpp b/dom/media/webrtc/transport/test/turn_unittest.cpp
new file mode 100644
index 0000000000..d9e6facc0b
--- /dev/null
+++ b/dom/media/webrtc/transport/test/turn_unittest.cpp
@@ -0,0 +1,440 @@
+/* -*- 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 "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 "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 "nricectx.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() = default;
+
+ static void SetUpTestCase() {
+ NrIceCtx::InitializeGlobals(NrIceCtx::GlobalConfig());
+ }
+
+ 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, nullptr, &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/dom/media/webrtc/transport/test/webrtcproxychannel_unittest.cpp b/dom/media/webrtc/transport/test/webrtcproxychannel_unittest.cpp
new file mode 100644
index 0000000000..234af9dac6
--- /dev/null
+++ b/dom/media/webrtc/transport/test/webrtcproxychannel_unittest.cpp
@@ -0,0 +1,739 @@
+/* -*- 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 <algorithm>
+#include <mutex>
+
+#include "mozilla/net/WebrtcTCPSocket.h"
+#include "mozilla/net/WebrtcTCPSocketCallback.h"
+
+#include "nsISocketTransport.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+static const uint32_t kDefaultTestTimeout = 2000;
+static const char kReadData[] = "Hello, World!";
+static const size_t kReadDataLength = sizeof(kReadData) - 1;
+static const std::string kReadDataString =
+ std::string(kReadData, kReadDataLength);
+static int kDataLargeOuterLoopCount = 128;
+static int kDataLargeInnerLoopCount = 1024;
+
+namespace mozilla {
+
+using namespace net;
+using namespace testing;
+
+class WebrtcTCPSocketTestCallback;
+
+class FakeSocketTransportProvider : public nsISocketTransport {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsISocketTransport
+ NS_IMETHOD GetHost(nsACString& aHost) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetPort(int32_t* aPort) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetScriptableOriginAttributes(
+ JSContext* cx, JS::MutableHandle<JS::Value> aOriginAttributes) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetScriptableOriginAttributes(
+ JSContext* cx, JS::Handle<JS::Value> aOriginAttributes) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ virtual nsresult GetOriginAttributes(
+ mozilla::OriginAttributes* _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ virtual nsresult SetOriginAttributes(
+ const mozilla::OriginAttributes& aOriginAttrs) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetPeerAddr(mozilla::net::NetAddr* _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetSelfAddr(mozilla::net::NetAddr* _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD Bind(mozilla::net::NetAddr* aLocalAddr) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetScriptablePeerAddr(nsINetAddr** _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetScriptableSelfAddr(nsINetAddr** _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetTlsSocketControl(
+ nsITLSSocketControl** aTLSSocketControl) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetSecurityCallbacks(
+ nsIInterfaceRequestor** aSecurityCallbacks) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetSecurityCallbacks(
+ nsIInterfaceRequestor* aSecurityCallbacks) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD IsAlive(bool* _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetTimeout(uint32_t aType, uint32_t* _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetTimeout(uint32_t aType, uint32_t aValue) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetLinger(bool aPolarity, int16_t aTimeout) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetReuseAddrPort(bool reuseAddrPort) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetConnectionFlags(uint32_t* aConnectionFlags) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetConnectionFlags(uint32_t aConnectionFlags) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetIsPrivate(bool) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetTlsFlags(uint32_t* aTlsFlags) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetTlsFlags(uint32_t aTlsFlags) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetQoSBits(uint8_t* aQoSBits) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetQoSBits(uint8_t aQoSBits) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetRecvBufferSize(uint32_t* aRecvBufferSize) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetSendBufferSize(uint32_t* aSendBufferSize) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetKeepaliveEnabled(bool* aKeepaliveEnabled) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetKeepaliveEnabled(bool aKeepaliveEnabled) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetKeepaliveVals(int32_t keepaliveIdleTime,
+ int32_t keepaliveRetryInterval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetResetIPFamilyPreference(
+ bool* aResetIPFamilyPreference) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetEchConfigUsed(bool* aEchConfigUsed) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetEchConfig(const nsACString& aEchConfig) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD ResolvedByTRR(bool* _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetRetryDnsIfPossible(bool* aRetryDns) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD GetStatus(nsresult* aStatus) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+
+ // nsITransport
+ NS_IMETHOD OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize,
+ uint32_t aSegmentCount,
+ nsIInputStream** _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD OpenOutputStream(uint32_t aFlags, uint32_t aSegmentSize,
+ uint32_t aSegmentCount,
+ nsIOutputStream** _retval) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+ NS_IMETHOD SetEventSink(nsITransportEventSink* aSink,
+ nsIEventTarget* aEventTarget) override {
+ MOZ_ASSERT(false);
+ return NS_OK;
+ }
+
+ // fake except for these methods which are OK to call
+ // nsISocketTransport
+ NS_IMETHOD SetRecvBufferSize(uint32_t aRecvBufferSize) override {
+ return NS_OK;
+ }
+ NS_IMETHOD SetSendBufferSize(uint32_t aSendBufferSize) override {
+ return NS_OK;
+ }
+ // nsITransport
+ NS_IMETHOD Close(nsresult aReason) override { return NS_OK; }
+
+ protected:
+ virtual ~FakeSocketTransportProvider() = default;
+};
+
+NS_IMPL_ISUPPORTS(FakeSocketTransportProvider, nsISocketTransport, nsITransport)
+
+// Implements some common elements to WebrtcTCPSocketTestOutputStream and
+// WebrtcTCPSocketTestInputStream.
+class WebrtcTCPSocketTestStream {
+ public:
+ WebrtcTCPSocketTestStream();
+
+ void Fail() { mMustFail = true; }
+
+ size_t DataLength();
+ template <typename T>
+ void AppendElements(const T* aBuffer, size_t aLength);
+
+ protected:
+ virtual ~WebrtcTCPSocketTestStream() = default;
+
+ nsTArray<uint8_t> mData;
+ std::mutex mDataMutex;
+
+ bool mMustFail;
+};
+
+WebrtcTCPSocketTestStream::WebrtcTCPSocketTestStream() : mMustFail(false) {}
+
+template <typename T>
+void WebrtcTCPSocketTestStream::AppendElements(const T* aBuffer,
+ size_t aLength) {
+ std::lock_guard<std::mutex> guard(mDataMutex);
+ mData.AppendElements(aBuffer, aLength);
+}
+
+size_t WebrtcTCPSocketTestStream::DataLength() {
+ std::lock_guard<std::mutex> guard(mDataMutex);
+ return mData.Length();
+}
+
+class WebrtcTCPSocketTestInputStream : public nsIAsyncInputStream,
+ public WebrtcTCPSocketTestStream {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIASYNCINPUTSTREAM
+ NS_DECL_NSIINPUTSTREAM
+
+ WebrtcTCPSocketTestInputStream()
+ : mMaxReadSize(1024 * 1024), mAllowCallbacks(false) {}
+
+ void DoCallback();
+ void CallCallback(const nsCOMPtr<nsIInputStreamCallback>& aCallback);
+ void AllowCallbacks() { mAllowCallbacks = true; }
+
+ size_t mMaxReadSize;
+
+ protected:
+ virtual ~WebrtcTCPSocketTestInputStream() = default;
+
+ private:
+ nsCOMPtr<nsIInputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mCallbackTarget;
+
+ bool mAllowCallbacks;
+};
+
+NS_IMPL_ISUPPORTS(WebrtcTCPSocketTestInputStream, nsIAsyncInputStream,
+ nsIInputStream)
+
+nsresult WebrtcTCPSocketTestInputStream::AsyncWait(
+ nsIInputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount, nsIEventTarget* aEventTarget) {
+ MOZ_ASSERT(!aEventTarget, "no event target should be set");
+
+ mCallback = aCallback;
+ mCallbackTarget = NS_GetCurrentThread();
+
+ if (mAllowCallbacks && DataLength() > 0) {
+ DoCallback();
+ }
+
+ return NS_OK;
+}
+
+nsresult WebrtcTCPSocketTestInputStream::CloseWithStatus(nsresult aStatus) {
+ return Close();
+}
+
+nsresult WebrtcTCPSocketTestInputStream::Close() { return NS_OK; }
+
+nsresult WebrtcTCPSocketTestInputStream::Available(uint64_t* aAvailable) {
+ *aAvailable = DataLength();
+ return NS_OK;
+}
+
+nsresult WebrtcTCPSocketTestInputStream::Read(char* aBuffer, uint32_t aCount,
+ uint32_t* aRead) {
+ std::lock_guard<std::mutex> guard(mDataMutex);
+ if (mMustFail) {
+ return NS_ERROR_FAILURE;
+ }
+ *aRead = std::min({(size_t)aCount, mData.Length(), mMaxReadSize});
+ memcpy(aBuffer, mData.Elements(), *aRead);
+ mData.RemoveElementsAt(0, *aRead);
+ return *aRead > 0 ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
+}
+
+nsresult WebrtcTCPSocketTestInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* _retval) {
+ MOZ_ASSERT(false);
+ return NS_OK;
+}
+
+nsresult WebrtcTCPSocketTestInputStream::IsNonBlocking(bool* aIsNonBlocking) {
+ *aIsNonBlocking = true;
+ return NS_OK;
+}
+
+void WebrtcTCPSocketTestInputStream::CallCallback(
+ const nsCOMPtr<nsIInputStreamCallback>& aCallback) {
+ aCallback->OnInputStreamReady(this);
+}
+
+void WebrtcTCPSocketTestInputStream::DoCallback() {
+ if (mCallback) {
+ mCallbackTarget->Dispatch(
+ NewRunnableMethod<const nsCOMPtr<nsIInputStreamCallback>&>(
+ "WebrtcTCPSocketTestInputStream::DoCallback", this,
+ &WebrtcTCPSocketTestInputStream::CallCallback,
+ std::move(mCallback)));
+
+ mCallbackTarget = nullptr;
+ }
+}
+
+class WebrtcTCPSocketTestOutputStream : public nsIAsyncOutputStream,
+ public WebrtcTCPSocketTestStream {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIASYNCOUTPUTSTREAM
+ NS_DECL_NSIOUTPUTSTREAM
+
+ WebrtcTCPSocketTestOutputStream() : mMaxWriteSize(1024 * 1024) {}
+
+ void DoCallback();
+ void CallCallback(const nsCOMPtr<nsIOutputStreamCallback>& aCallback);
+
+ std::string DataString();
+
+ uint32_t mMaxWriteSize;
+
+ protected:
+ virtual ~WebrtcTCPSocketTestOutputStream() = default;
+
+ private:
+ nsCOMPtr<nsIOutputStreamCallback> mCallback;
+ nsCOMPtr<nsIEventTarget> mCallbackTarget;
+};
+
+NS_IMPL_ISUPPORTS(WebrtcTCPSocketTestOutputStream, nsIAsyncOutputStream,
+ nsIOutputStream)
+
+nsresult WebrtcTCPSocketTestOutputStream::AsyncWait(
+ nsIOutputStreamCallback* aCallback, uint32_t aFlags,
+ uint32_t aRequestedCount, nsIEventTarget* aEventTarget) {
+ MOZ_ASSERT(!aEventTarget, "no event target should be set");
+
+ mCallback = aCallback;
+ mCallbackTarget = NS_GetCurrentThread();
+
+ return NS_OK;
+}
+
+nsresult WebrtcTCPSocketTestOutputStream::CloseWithStatus(nsresult aStatus) {
+ return Close();
+}
+
+nsresult WebrtcTCPSocketTestOutputStream::Close() { return NS_OK; }
+
+nsresult WebrtcTCPSocketTestOutputStream::Flush() { return NS_OK; }
+
+nsresult WebrtcTCPSocketTestOutputStream::Write(const char* aBuffer,
+ uint32_t aCount,
+ uint32_t* aWrote) {
+ if (mMustFail) {
+ return NS_ERROR_FAILURE;
+ }
+ *aWrote = std::min(aCount, mMaxWriteSize);
+ AppendElements(aBuffer, *aWrote);
+ return NS_OK;
+}
+
+nsresult WebrtcTCPSocketTestOutputStream::WriteSegments(
+ nsReadSegmentFun aReader, void* aClosure, uint32_t aCount,
+ uint32_t* _retval) {
+ MOZ_ASSERT(false);
+ return NS_OK;
+}
+
+nsresult WebrtcTCPSocketTestOutputStream::WriteFrom(nsIInputStream* aFromStream,
+ uint32_t aCount,
+ uint32_t* _retval) {
+ MOZ_ASSERT(false);
+ return NS_OK;
+}
+
+nsresult WebrtcTCPSocketTestOutputStream::IsNonBlocking(bool* aIsNonBlocking) {
+ *aIsNonBlocking = true;
+ return NS_OK;
+}
+
+void WebrtcTCPSocketTestOutputStream::CallCallback(
+ const nsCOMPtr<nsIOutputStreamCallback>& aCallback) {
+ aCallback->OnOutputStreamReady(this);
+}
+
+void WebrtcTCPSocketTestOutputStream::DoCallback() {
+ if (mCallback) {
+ mCallbackTarget->Dispatch(
+ NewRunnableMethod<const nsCOMPtr<nsIOutputStreamCallback>&>(
+ "WebrtcTCPSocketTestOutputStream::CallCallback", this,
+ &WebrtcTCPSocketTestOutputStream::CallCallback,
+ std::move(mCallback)));
+
+ mCallbackTarget = nullptr;
+ }
+}
+
+std::string WebrtcTCPSocketTestOutputStream::DataString() {
+ std::lock_guard<std::mutex> guard(mDataMutex);
+ return std::string((char*)mData.Elements(), mData.Length());
+}
+
+// Fake as in not the real WebrtcTCPSocket but real enough
+class FakeWebrtcTCPSocket : public WebrtcTCPSocket {
+ public:
+ explicit FakeWebrtcTCPSocket(WebrtcTCPSocketCallback* aCallback)
+ : WebrtcTCPSocket(aCallback) {}
+
+ protected:
+ virtual ~FakeWebrtcTCPSocket() = default;
+
+ void InvokeOnClose(nsresult aReason) override;
+ void InvokeOnConnected() override;
+ void InvokeOnRead(nsTArray<uint8_t>&& aReadData) override;
+};
+
+void FakeWebrtcTCPSocket::InvokeOnClose(nsresult aReason) {
+ mProxyCallbacks->OnClose(aReason);
+}
+
+void FakeWebrtcTCPSocket::InvokeOnConnected() {
+ mProxyCallbacks->OnConnected("http"_ns);
+}
+
+void FakeWebrtcTCPSocket::InvokeOnRead(nsTArray<uint8_t>&& aReadData) {
+ mProxyCallbacks->OnRead(std::move(aReadData));
+}
+
+class WebrtcTCPSocketTest : public MtransportTest {
+ public:
+ WebrtcTCPSocketTest()
+ : MtransportTest(),
+ mSocketThread(nullptr),
+ mSocketTransport(nullptr),
+ mInputStream(nullptr),
+ mOutputStream(nullptr),
+ mChannel(nullptr),
+ mCallback(nullptr),
+ mOnCloseCalled(false),
+ mOnConnectedCalled(false) {}
+
+ // WebrtcTCPSocketCallback forwards from mCallback
+ void OnClose(nsresult aReason);
+ void OnConnected(const nsACString& aProxyType);
+ void OnRead(nsTArray<uint8_t>&& aReadData);
+
+ void SetUp() override;
+ void TearDown() override;
+
+ void DoTransportAvailable();
+
+ std::string ReadDataAsString();
+ std::string GetDataLarge();
+
+ nsCOMPtr<nsIEventTarget> mSocketThread;
+
+ nsCOMPtr<nsISocketTransport> mSocketTransport;
+ RefPtr<WebrtcTCPSocketTestInputStream> mInputStream;
+ RefPtr<WebrtcTCPSocketTestOutputStream> mOutputStream;
+ RefPtr<FakeWebrtcTCPSocket> mChannel;
+ RefPtr<WebrtcTCPSocketTestCallback> mCallback;
+
+ bool mOnCloseCalled;
+ bool mOnConnectedCalled;
+
+ size_t ReadDataLength();
+ template <typename T>
+ void AppendReadData(const T* aBuffer, size_t aLength);
+
+ private:
+ nsTArray<uint8_t> mReadData;
+ std::mutex mReadDataMutex;
+};
+
+class WebrtcTCPSocketTestCallback : public WebrtcTCPSocketCallback {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketTestCallback, override)
+
+ explicit WebrtcTCPSocketTestCallback(WebrtcTCPSocketTest* aTest)
+ : mTest(aTest) {}
+
+ // WebrtcTCPSocketCallback
+ void OnClose(nsresult aReason) override;
+ void OnConnected(const nsACString& aProxyType) override;
+ void OnRead(nsTArray<uint8_t>&& aReadData) override;
+
+ protected:
+ virtual ~WebrtcTCPSocketTestCallback() = default;
+
+ private:
+ WebrtcTCPSocketTest* mTest;
+};
+
+void WebrtcTCPSocketTest::SetUp() {
+ nsresult rv;
+ // WebrtcTCPSocket's threading model is the same as mtransport
+ // all socket operations are done on the socket thread
+ // callbacks are invoked on the main thread
+ mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ mSocketTransport = new FakeSocketTransportProvider();
+ mInputStream = new WebrtcTCPSocketTestInputStream();
+ mOutputStream = new WebrtcTCPSocketTestOutputStream();
+ mCallback = new WebrtcTCPSocketTestCallback(this);
+ mChannel = new FakeWebrtcTCPSocket(mCallback.get());
+}
+
+void WebrtcTCPSocketTest::TearDown() {}
+
+// WebrtcTCPSocketCallback
+void WebrtcTCPSocketTest::OnRead(nsTArray<uint8_t>&& aReadData) {
+ AppendReadData(aReadData.Elements(), aReadData.Length());
+}
+
+void WebrtcTCPSocketTest::OnConnected(const nsACString& aProxyType) {
+ mOnConnectedCalled = true;
+}
+
+void WebrtcTCPSocketTest::OnClose(nsresult aReason) { mOnCloseCalled = true; }
+
+void WebrtcTCPSocketTest::DoTransportAvailable() {
+ if (!mSocketThread->IsOnCurrentThread()) {
+ mSocketThread->Dispatch(
+ NS_NewRunnableFunction("DoTransportAvailable", [this]() -> void {
+ nsresult rv;
+ rv = mChannel->OnTransportAvailable(mSocketTransport, mInputStream,
+ mOutputStream);
+ ASSERT_EQ(NS_OK, rv);
+ }));
+ } else {
+ // should always be called on the main thread
+ MOZ_ASSERT(0);
+ }
+}
+
+std::string WebrtcTCPSocketTest::ReadDataAsString() {
+ std::lock_guard<std::mutex> guard(mReadDataMutex);
+ return std::string((char*)mReadData.Elements(), mReadData.Length());
+}
+
+std::string WebrtcTCPSocketTest::GetDataLarge() {
+ std::string data;
+ for (int i = 0; i < kDataLargeOuterLoopCount * kDataLargeInnerLoopCount;
+ ++i) {
+ data += kReadData;
+ }
+ return data;
+}
+
+template <typename T>
+void WebrtcTCPSocketTest::AppendReadData(const T* aBuffer, size_t aLength) {
+ std::lock_guard<std::mutex> guard(mReadDataMutex);
+ mReadData.AppendElements(aBuffer, aLength);
+}
+
+size_t WebrtcTCPSocketTest::ReadDataLength() {
+ std::lock_guard<std::mutex> guard(mReadDataMutex);
+ return mReadData.Length();
+}
+
+void WebrtcTCPSocketTestCallback::OnClose(nsresult aReason) {
+ mTest->OnClose(aReason);
+}
+
+void WebrtcTCPSocketTestCallback::OnConnected(const nsACString& aProxyType) {
+ mTest->OnConnected(aProxyType);
+}
+
+void WebrtcTCPSocketTestCallback::OnRead(nsTArray<uint8_t>&& aReadData) {
+ mTest->OnRead(std::move(aReadData));
+}
+
+} // namespace mozilla
+
+typedef mozilla::WebrtcTCPSocketTest WebrtcTCPSocketTest;
+
+TEST_F(WebrtcTCPSocketTest, SetUp) {}
+
+TEST_F(WebrtcTCPSocketTest, TransportAvailable) {
+ DoTransportAvailable();
+ ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout);
+}
+
+TEST_F(WebrtcTCPSocketTest, Read) {
+ DoTransportAvailable();
+ ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout);
+
+ mInputStream->AppendElements(kReadData, kReadDataLength);
+ mInputStream->DoCallback();
+
+ ASSERT_TRUE_WAIT(ReadDataAsString() == kReadDataString, kDefaultTestTimeout);
+}
+
+TEST_F(WebrtcTCPSocketTest, Write) {
+ DoTransportAvailable();
+ ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout);
+
+ nsTArray<uint8_t> data;
+ data.AppendElements(kReadData, kReadDataLength);
+ mChannel->Write(std::move(data));
+
+ ASSERT_TRUE_WAIT(mChannel->CountUnwrittenBytes() == kReadDataLength,
+ kDefaultTestTimeout);
+
+ mOutputStream->DoCallback();
+
+ ASSERT_TRUE_WAIT(mOutputStream->DataString() == kReadDataString,
+ kDefaultTestTimeout);
+}
+
+TEST_F(WebrtcTCPSocketTest, ReadFail) {
+ DoTransportAvailable();
+ ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout);
+
+ mInputStream->AppendElements(kReadData, kReadDataLength);
+ mInputStream->Fail();
+ mInputStream->DoCallback();
+
+ ASSERT_TRUE_WAIT(mOnCloseCalled, kDefaultTestTimeout);
+ ASSERT_EQ(0U, ReadDataLength());
+}
+
+TEST_F(WebrtcTCPSocketTest, WriteFail) {
+ DoTransportAvailable();
+ ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout);
+
+ nsTArray<uint8_t> array;
+ array.AppendElements(kReadData, kReadDataLength);
+ mChannel->Write(std::move(array));
+
+ ASSERT_TRUE_WAIT(mChannel->CountUnwrittenBytes() == kReadDataLength,
+ kDefaultTestTimeout);
+
+ mOutputStream->Fail();
+ mOutputStream->DoCallback();
+
+ ASSERT_TRUE_WAIT(mOnCloseCalled, kDefaultTestTimeout);
+ ASSERT_EQ(0U, mOutputStream->DataLength());
+}
+
+TEST_F(WebrtcTCPSocketTest, ReadLarge) {
+ DoTransportAvailable();
+ ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout);
+
+ const std::string data = GetDataLarge();
+
+ mInputStream->AppendElements(data.c_str(), data.length());
+ // make sure reading loops more than once
+ mInputStream->mMaxReadSize = 3072;
+ mInputStream->AllowCallbacks();
+ mInputStream->DoCallback();
+
+ ASSERT_TRUE_WAIT(ReadDataAsString() == data, kDefaultTestTimeout);
+}
+
+TEST_F(WebrtcTCPSocketTest, WriteLarge) {
+ DoTransportAvailable();
+ ASSERT_TRUE_WAIT(mOnConnectedCalled, kDefaultTestTimeout);
+
+ const std::string data = GetDataLarge();
+
+ for (int i = 0; i < kDataLargeOuterLoopCount; ++i) {
+ nsTArray<uint8_t> array;
+ int chunkSize = kReadDataString.length() * kDataLargeInnerLoopCount;
+ int offset = i * chunkSize;
+ array.AppendElements(data.c_str() + offset, chunkSize);
+ mChannel->Write(std::move(array));
+ }
+
+ ASSERT_TRUE_WAIT(mChannel->CountUnwrittenBytes() == data.length(),
+ kDefaultTestTimeout);
+
+ // make sure writing loops more than once per write request
+ mOutputStream->mMaxWriteSize = 1024;
+ mOutputStream->DoCallback();
+
+ ASSERT_TRUE_WAIT(mOutputStream->DataString() == data, kDefaultTestTimeout);
+}
diff --git a/dom/media/webrtc/transport/test_nr_socket.cpp b/dom/media/webrtc/transport/test_nr_socket.cpp
new file mode 100644
index 0000000000..064013a8b7
--- /dev/null
+++ b/dom/media/webrtc/transport/test_nr_socket.cpp
@@ -0,0 +1,1135 @@
+/* -*- 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 "async_wait.h"
+#include "async_timer.h"
+#include "nr_socket.h"
+#include "stun.h"
+#include "transport_addr.h"
+}
+
+#include "mozilla/RefPtr.h"
+#include "test_nr_socket.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.empty() || !type.compare("ENDPOINT_INDEPENDENT")) {
+ return TestNat::ENDPOINT_INDEPENDENT;
+ }
+ if (!type.compare("ADDRESS_DEPENDENT")) {
+ return TestNat::ADDRESS_DEPENDENT;
+ }
+ 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!");
+ }
+
+ if (!nr_transport_addr_cmp(&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;
+}
+
+void TestNat::set_proxy_config(
+ std::shared_ptr<NrSocketProxyConfig> aProxyConfig) {
+ proxy_config_ = std::move(aProxyConfig);
+}
+
+TestNrSocket::TestNrSocket(TestNat* nat)
+ : nat_(nat), tls_(false), 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.
+ if ((r = nr_transport_addr_copy(&nat_external_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,
+ nat_->proxy_config_);
+
+ 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) {
+ tls_ = addr->tls;
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p create %s", this,
+ addr->as_string);
+ return NrSocketBase::CreateSocket(addr, &internal_socket_, nullptr);
+}
+
+int TestNrSocket::getaddr(nr_transport_addr* addrp) {
+ return internal_socket_->getaddr(addrp);
+}
+
+void TestNrSocket::close() {
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s closing", this,
+ internal_socket_->my_addr().as_string);
+ if (timer_handle_) {
+ NR_async_timer_cancel(timer_handle_);
+ timer_handle_ = nullptr;
+ }
+ 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 %p %s listening", this,
+ 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,
+ const nr_transport_addr* to) {
+ MOZ_ASSERT(internal_socket_->my_addr().protocol != IPPROTO_TCP);
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s %s", this, __FUNCTION__,
+ to->as_string);
+
+ if (nat_->nat_delegate_ &&
+ nat_->nat_delegate_->on_sendto(nat_, msg, len, flags, to)) {
+ return nat_->error_code_for_drop_;
+ }
+
+ UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg));
+ if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
+ return nat_->error_code_for_drop_;
+ }
+
+ if (nr_is_stun_request_message(buf, len) &&
+ maybe_send_fake_response(buf, len, to)) {
+ 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_) {
+ return nat_->error_code_for_drop_;
+ }
+
+ // 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);
+
+ if (!read_buffer_.empty()) {
+ UdpPacket& packet = read_buffer_.front();
+ *len = std::min(maxlen, packet.buffer_->len());
+ memcpy(buf, packet.buffer_->data(), *len);
+ nr_transport_addr_copy(from, &packet.remote_address_);
+ read_buffer_.pop_front();
+ return 0;
+ }
+
+ 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);
+ const nr_transport_addr to = readable_socket_->my_addr();
+ readable_socket_ = nullptr;
+ if (!r) {
+ PortMapping* port_mapping_used;
+ ingress_allowed = allow_ingress(to, *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 bad 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& to,
+ 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));
+
+ // Find the port mapping (if any) that this packet landed on
+ for (PortMapping* port_mapping : port_mappings_) {
+ if (!nr_transport_addr_cmp(&to, &port_mapping->external_socket_->my_addr(),
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ *port_mapping_used = port_mapping;
+ }
+ }
+
+ if (NS_WARN_IF(!(*port_mapping_used))) {
+ MOZ_ASSERT(false);
+ r_log(LOG_GENERIC, LOG_INFO,
+ "TestNrSocket %s denying ingress from %s: "
+ "No port mapping for this local port! What?",
+ internal_socket_->my_addr().as_string, from.as_string);
+ return false;
+ }
+
+ if (!port_mapping_matches(**port_mapping_used, from, nat_->filtering_type_)) {
+ r_log(LOG_GENERIC, LOG_INFO,
+ "TestNrSocket %s denying ingress from %s: "
+ "Filtered (no port mapping for source)",
+ 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(const nr_transport_addr* addr) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s connecting to %s", this,
+ internal_socket_->my_addr().as_string, addr->as_string);
+
+ if (connect_invoked_ || !port_mappings_.empty()) {
+ MOZ_CRASH("TestNrSocket::connect() called more than once!");
+ return R_INTERNAL;
+ }
+
+ if (maybe_get_redirect_targets(addr).isSome()) {
+ // If we are simulating STUN redirects for |addr|, we need to pretend that
+ // the TCP connection worked, since |addr| probably does not actually point
+ // at something that exists.
+ connect_fake_stun_address_.reset(new nr_transport_addr);
+ nr_transport_addr_copy(connect_fake_stun_address_.get(), addr);
+
+ // We dispatch this, otherwise nICEr can trip over its shoelaces
+ GetCurrentSerialEventTarget()->Dispatch(
+ NS_NewRunnableFunction("Async writeable callback for TestNrSocket",
+ [this, self = RefPtr<TestNrSocket>(this)] {
+ if (poll_flags() & PR_POLL_WRITE) {
+ fire_callback(NR_ASYNC_WAIT_WRITE);
+ }
+ }));
+
+ return R_WOULDBLOCK;
+ }
+
+ 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) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s writing", this,
+ internal_socket_->my_addr().as_string);
+
+ UCHAR* buf = static_cast<UCHAR*>(const_cast<void*>(msg));
+
+ if (nat_->nat_delegate_ &&
+ nat_->nat_delegate_->on_write(nat_, msg, len, written)) {
+ return R_INTERNAL;
+ }
+
+ if (nat_->block_stun_ && nr_is_stun_message(buf, len)) {
+ // Should cause this socket to be abandoned
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %s dropping outgoing TCP "
+ "because it is configured to drop STUN",
+ my_addr().as_string);
+ return R_INTERNAL;
+ }
+
+ if (nr_is_stun_request_message(buf, len) && connect_fake_stun_address_ &&
+ maybe_send_fake_response(buf, len, connect_fake_stun_address_.get())) {
+ return 0;
+ }
+
+ if (nat_->block_tcp_ && !tls_) {
+ // Should cause this socket to be abandoned
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %s dropping outgoing TCP "
+ "because it is configured to drop TCP",
+ my_addr().as_string);
+ return R_INTERNAL;
+ }
+
+ if (nat_->block_tls_ && tls_) {
+ // Should cause this socket to be abandoned
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %s dropping outgoing TLS "
+ "because it is configured to drop TLS",
+ my_addr().as_string);
+ return R_INTERNAL;
+ }
+
+ 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);
+ }
+ destroy_stale_port_mappings();
+ if (port_mappings_.empty()) {
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %s dropping outgoing TCP "
+ "because the port mapping was stale",
+ my_addr().as_string);
+ return R_INTERNAL;
+ }
+ // 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) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket %p %s reading", this,
+ internal_socket_->my_addr().as_string);
+
+ if (!read_buffer_.empty()) {
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %p %s has stuff in read_buffer_", this,
+ internal_socket_->my_addr().as_string);
+ UdpPacket packet(std::move(read_buffer_.front()));
+ read_buffer_.pop_front();
+ *len = std::min(maxlen, packet.buffer_->len());
+ memcpy(buf, packet.buffer_->data(), *len);
+ if (*len != packet.buffer_->len()) {
+ // Put remaining bytes in new packet, at the front.
+ read_buffer_.emplace_front(packet.buffer_->data() + *len,
+ packet.buffer_->len() - *len,
+ packet.remote_address_);
+ }
+ return 0;
+ }
+
+ if (connect_fake_stun_address_) {
+ return R_WOULDBLOCK;
+ }
+
+ int r;
+
+ if (port_mappings_.empty()) {
+ r = internal_socket_->read(buf, maxlen, len);
+ } else {
+ MOZ_ASSERT(port_mappings_.size() == 1);
+ r = port_mappings_.front()->external_socket_->read(buf, maxlen, len);
+ if (!r && nat_->refresh_on_ingress_) {
+ port_mappings_.front()->last_used_ = PR_IntervalNow();
+ }
+ }
+
+ if (r) {
+ return r;
+ }
+
+ if (nat_->nat_delegate_ &&
+ nat_->nat_delegate_->on_read(nat_, buf, maxlen, len)) {
+ return R_INTERNAL;
+ }
+
+ if (nat_->block_tcp_ && !tls_) {
+ // Should cause this socket to be abandoned
+ return R_INTERNAL;
+ }
+
+ if (nat_->block_tls_ && tls_) {
+ // Should cause this socket to be abandoned
+ return R_INTERNAL;
+ }
+
+ UCHAR* cbuf = static_cast<UCHAR*>(const_cast<void*>(buf));
+ if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) {
+ // Should cause this socket to be abandoned
+ return R_INTERNAL;
+ }
+
+ return r;
+}
+
+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);
+ if (!read_buffer_.empty()) {
+ fire_readable_callback();
+ return 0;
+ }
+
+ // 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 {
+ if (connect_fake_stun_address_) {
+ // Fake TCP connection case; register the callback on this socket, not
+ // a real one.
+ return NrSocketBase::async_wait(how, cb, cb_arg, function, line);
+ }
+
+ // 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);
+ }
+ 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");
+
+ if (connect_fake_stun_address_) {
+ return NrSocketBase::cancel(how);
+ }
+
+ // 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!");
+ }
+
+ if (!nr_transport_addr_cmp(&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 %p %s ready for read", this,
+ 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 {
+ for (PortMapping* port_mapping : port_mappings_) {
+ if (port_mapping_matches(*port_mapping, remote_address, filter)) {
+ return port_mapping;
+ }
+ }
+ return nullptr;
+}
+
+/* static */
+bool TestNrSocket::port_mapping_matches(const PortMapping& port_mapping,
+ const nr_transport_addr& remote_addr,
+ TestNat::NatBehavior filter) {
+ 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;
+ }
+
+ return !nr_transport_addr_cmp(&remote_addr, &port_mapping.remote_address_,
+ compare_flags);
+}
+
+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) {
+ nr_transport_addr_copy(&remote_address_, &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, &to);
+
+ if (r == R_WOULDBLOCK) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "Enqueueing UDP packet to %s", to.as_string);
+ send_queue_.emplace_back(msg, len, to);
+ return 0;
+ }
+ 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);
+}
+
+class nr_stun_message_deleter {
+ public:
+ nr_stun_message_deleter() = default;
+ void operator()(nr_stun_message* msg) const { nr_stun_message_destroy(&msg); }
+};
+
+bool TestNrSocket::maybe_send_fake_response(const void* msg, size_t len,
+ const nr_transport_addr* to) {
+ Maybe<nsTArray<nsCString>> redirect_targets = maybe_get_redirect_targets(to);
+ if (!redirect_targets.isSome()) {
+ return false;
+ }
+
+ std::unique_ptr<nr_stun_message, nr_stun_message_deleter> request;
+ {
+ nr_stun_message* temp = nullptr;
+ if (NS_WARN_IF(nr_stun_message_create2(&temp, (unsigned char*)msg, len))) {
+ return false;
+ }
+ request.reset(temp);
+ }
+
+ if (NS_WARN_IF(nr_stun_decode_message(request.get(), nullptr, nullptr))) {
+ return false;
+ }
+
+ std::unique_ptr<nr_stun_message, nr_stun_message_deleter> response;
+ {
+ nr_stun_message* temp = nullptr;
+ if (nr_stun_message_create(&temp)) {
+ MOZ_CRASH("nr_stun_message_create failed!");
+ }
+ response.reset(temp);
+ }
+
+ nr_stun_form_error_response(request.get(), response.get(), 300,
+ (char*)"Try alternate");
+
+ int port = 0;
+ if (nr_transport_addr_get_port(to, &port)) {
+ MOZ_CRASH();
+ }
+
+ for (const nsCString& address : *redirect_targets) {
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket attempting to add alternate server %s", address.Data());
+ nr_transport_addr addr;
+ if (NS_WARN_IF(nr_str_port_to_transport_addr(address.Data(), port,
+ IPPROTO_UDP, &addr))) {
+ continue;
+ }
+ if (nr_stun_message_add_alternate_server_attribute(response.get(), &addr)) {
+ MOZ_CRASH("nr_stun_message_add_alternate_server_attribute failed!");
+ }
+ }
+
+ if (nr_stun_encode_message(response.get())) {
+ MOZ_CRASH("nr_stun_encode_message failed!");
+ }
+
+ nr_transport_addr response_from;
+ if (nr_transport_addr_is_wildcard(to)) {
+ // |to| points to an FQDN, and nICEr is delegating DNS lookup to us; we
+ // aren't _actually_ going to do that though, so we select a bogus address
+ // for the response to come from. TEST-NET is a fairly reasonable thing to
+ // use for this.
+ int port = 0;
+ if (nr_transport_addr_get_port(to, &port)) {
+ MOZ_CRASH();
+ }
+ switch (to->ip_version) {
+ case NR_IPV4:
+ if (nr_str_port_to_transport_addr("198.51.100.1", port, to->protocol,
+ &response_from)) {
+ MOZ_CRASH();
+ }
+ break;
+ case NR_IPV6:
+ if (nr_str_port_to_transport_addr("::ffff:198.51.100.1", port,
+ to->protocol, &response_from)) {
+ MOZ_CRASH();
+ }
+ break;
+ default:
+ MOZ_CRASH();
+ }
+ } else {
+ nr_transport_addr_copy(&response_from, to);
+ }
+
+ read_buffer_.emplace_back(response->buffer, response->length, response_from);
+
+ // We dispatch this, otherwise nICEr can trip over its shoelaces
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %p scheduling callback for redirect response", this);
+ GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
+ "Async readable callback for TestNrSocket",
+ [this, self = RefPtr<TestNrSocket>(this)] {
+ if (poll_flags() & PR_POLL_READ) {
+ fire_readable_callback();
+ } else {
+ r_log(LOG_GENERIC, LOG_DEBUG,
+ "TestNrSocket %p deferring callback for redirect response",
+ this);
+ }
+ }));
+
+ return true;
+}
+
+Maybe<nsTArray<nsCString>> TestNrSocket::maybe_get_redirect_targets(
+ const nr_transport_addr* to) const {
+ Maybe<nsTArray<nsCString>> result;
+
+ // 256 is overkill, but it hardly matters
+ char addrstring[256];
+ if (nr_transport_addr_get_addrstring(to, addrstring, 256)) {
+ MOZ_CRASH("nr_transport_addr_get_addrstring failed!");
+ }
+
+ r_log(LOG_GENERIC, LOG_DEBUG, "TestNrSocket checking redirect rules for %s",
+ addrstring);
+ auto it = nat_->stun_redirect_map_.find(nsCString(addrstring));
+ if (it != nat_->stun_redirect_map_.end()) {
+ result = Some(it->second);
+ }
+
+ return result;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/test_nr_socket.h b/dom/media/webrtc/transport/test_nr_socket.h
new file mode 100644
index 0000000000..c6a796c063
--- /dev/null
+++ b/dom/media/webrtc/transport/test_nr_socket.h
@@ -0,0 +1,370 @@
+/* -*- 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 <map>
+#include <list>
+#include <string>
+
+#include "mozilla/UniquePtr.h"
+#include "prinrval.h"
+#include "mediapacket.h"
+
+namespace mozilla {
+
+class TestNrSocket;
+class NrSocketProxyConfig;
+
+/**
+ * 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:
+ /**
+ * This allows TestNat traffic to be passively inspected.
+ * If a non-zero (error) value is returned, the packet will be dropped,
+ * allowing for tests to extend how packet manipulation is done by
+ * TestNat with having to modify TestNat itself.
+ */
+ class NatDelegate {
+ public:
+ virtual int on_read(TestNat* nat, void* buf, size_t maxlen,
+ size_t* len) = 0;
+ virtual int on_sendto(TestNat* nat, const void* msg, size_t len, int flags,
+ const nr_transport_addr* to) = 0;
+ virtual int on_write(TestNat* nat, const void* msg, size_t len,
+ size_t* written) = 0;
+ };
+
+ 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),
+ block_tcp_(false),
+ block_tls_(false),
+ error_code_for_drop_(0),
+ delay_stun_resp_ms_(0),
+ nat_delegate_(nullptr),
+ 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);
+
+ void set_proxy_config(std::shared_ptr<NrSocketProxyConfig> aProxyConfig);
+
+ 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_;
+ bool block_tcp_;
+ bool block_tls_;
+ bool error_code_for_drop_;
+ /* Note: this can only delay a single response so far (bug 1253657) */
+ uint32_t delay_stun_resp_ms_;
+
+ // When we see an outgoing STUN request with a destination address or
+ // destination FQDN that matches a key in this map, we respond with a STUN/300
+ // with a list of ALTERNATE-SERVER fields based on the value in this map.
+ std::map<nsCString, CopyableTArray<nsCString>> stun_redirect_map_;
+
+ NatDelegate* nat_delegate_;
+ std::shared_ptr<NrSocketProxyConfig> proxy_config_;
+
+ private:
+ std::set<TestNrSocket*> sockets_;
+
+ ~TestNat() = default;
+};
+
+/**
+ * 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,
+ const 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(const 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 MediaPacket) {
+ buffer_->Copy(static_cast<const uint8_t*>(msg), len);
+ nr_transport_addr_copy(&remote_address_, &addr);
+ }
+
+ UdpPacket(UdpPacket&& aOrig) = default;
+
+ ~UdpPacket() = default;
+
+ nr_transport_addr remote_address_;
+ UniquePtr<MediaPacket> buffer_;
+ };
+
+ 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<UdpPacket> send_queue_;
+ };
+
+ struct DeferredPacket {
+ DeferredPacket(TestNrSocket* sock, const void* data, size_t len, int flags,
+ const nr_transport_addr* addr,
+ RefPtr<NrSocketBase> internal_socket)
+ : socket_(sock),
+ buffer_(),
+ flags_(flags),
+ internal_socket_(internal_socket) {
+ buffer_.Copy(reinterpret_cast<const uint8_t*>(data), len);
+ nr_transport_addr_copy(&to_, addr);
+ }
+
+ TestNrSocket* socket_;
+ MediaPacket 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& to, 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;
+ static bool port_mapping_matches(const PortMapping& port_mapping,
+ const nr_transport_addr& remote_addr,
+ TestNat::NatBehavior filter);
+ 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);
+
+ bool maybe_send_fake_response(const void* msg, size_t len,
+ const nr_transport_addr* to);
+ Maybe<nsTArray<nsCString>> maybe_get_redirect_targets(
+ const nr_transport_addr* to) const;
+
+ 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_;
+ bool tls_;
+ // 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_;
+
+ // Just used for fake stun responses right now. Not _necessarily_ just UDP
+ // stuff, UdpPacket just has what we need to make this work for UDP.
+ std::list<UdpPacket> read_buffer_;
+ std::unique_ptr<nr_transport_addr> connect_fake_stun_address_;
+};
+
+} // namespace mozilla
+
+#endif // test_nr_socket__
diff --git a/dom/media/webrtc/transport/third_party/import.py b/dom/media/webrtc/transport/third_party/import.py
new file mode 100644
index 0000000000..6defdf26b7
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/moz.build b/dom/media/webrtc/transport/third_party/moz.build
new file mode 100644
index 0000000000..40de3c1ebb
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/moz.build
@@ -0,0 +1,40 @@
+# -*- 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',
+]
+
+# Set gyp vars that webrtc needs when building under various analysis tools.
+# Primarily this prevents webrtc from setting NVALGRIND and breaking builds.
+gyp_vars_copy = gyp_vars.copy()
+if CONFIG['MOZ_VALGRIND']:
+ gyp_vars_copy.update(build_for_tool="memcheck")
+elif CONFIG['MOZ_ASAN']:
+ gyp_vars_copy.update(build_for_tool="asan")
+elif CONFIG['MOZ_TSAN']:
+ gyp_vars_copy.update(build_for_tool="tsan")
+
+# These files cannot be built in unified mode because of name clashes on RCSSTRING
+
+GYP_DIRS['nICEr'].input = 'nICEr/nicer.gyp'
+GYP_DIRS['nICEr'].variables = gyp_vars_copy
+GYP_DIRS['nICEr'].sandbox_vars['REQUIRES_UNIFIED_BUILD'] = True
+GYP_DIRS['nICEr'].sandbox_vars['FINAL_LIBRARY'] = 'xul'
+GYP_DIRS['nrappkit'].input = 'nrappkit/nrappkit.gyp'
+GYP_DIRS['nrappkit'].variables = gyp_vars_copy
+GYP_DIRS['nrappkit'].sandbox_vars['FINAL_LIBRARY'] = 'xul'
+
+
+if CONFIG["CC_TYPE"] == "gcc":
+ GYP_DIRS['nICEr'].sandbox_vars['CFLAGS'] = ['-Wno-stringop-overflow']
+ GYP_DIRS['nrappkit'].sandbox_vars['CFLAGS'] = ['-Wno-stringop-overflow']
+ if int(CONFIG["CC_VERSION"].split(".")[0]) >= 8:
+ GYP_DIRS['nICEr'].sandbox_vars['CFLAGS'] += ['-Wno-stringop-truncation']
+ GYP_DIRS['nrappkit'].sandbox_vars['CFLAGS'] += ['-Wno-stringop-truncation']
diff --git a/dom/media/webrtc/transport/third_party/nICEr/IMPORT_FILES b/dom/media/webrtc/transport/third_party/nICEr/IMPORT_FILES
new file mode 100644
index 0000000000..c9b3938a49
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/IMPORT_FILES
@@ -0,0 +1,73 @@
+ # 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/addrs-bsd.c
+ ./src/stun/addrs-bsd.h
+ ./src/stun/addrs-netlink.c
+ ./src/stun/addrs-netlink.h
+ ./src/stun/addrs-win32.c
+ ./src/stun/addrs-win32.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
diff --git a/dom/media/webrtc/transport/third_party/nICEr/README b/dom/media/webrtc/transport/third_party/nICEr/README
new file mode 100644
index 0000000000..390203dec5
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/README_MOZILLA b/dom/media/webrtc/transport/third_party/nICEr/README_MOZILLA
new file mode 100644
index 0000000000..8e058ac7da
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/nicer.gyp b/dom/media/webrtc/transport/third_party/nICEr/nicer.gyp
new file mode 100644
index 0000000000..488b9b229c
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/nicer.gyp
@@ -0,0 +1,276 @@
+# 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' : {
+ '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_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/addrs-bsd.c",
+ "./src/stun/addrs-bsd.h",
+ "./src/stun/addrs-netlink.c",
+ "./src/stun/addrs-netlink.h",
+ "./src/stun/addrs-win32.c",
+ "./src/stun/addrs-win32.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",
+
+
+ ],
+
+ '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',
+ '-Wno-format',
+ '-Wno-format-security',
+ ],
+ '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',
+ '_WINSOCK_DEPRECATED_NO_WARNINGS',
+ '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'
+ ],
+ }],
+
+ # Windows, clang-cl build
+ [ 'clang_cl == 1', {
+ 'cflags_mozilla': [
+ '-Xclang',
+ '-Wall',
+ '-Xclang',
+ '-Wno-parentheses',
+ '-Wno-pointer-sign',
+ '-Wno-strict-prototypes',
+ '-Xclang',
+ '-Wno-unused-function',
+ '-Wmissing-prototypes',
+ '-Wno-format',
+ '-Wno-format-security',
+ ],
+ }],
+
+ ## Linux/Android
+ [ '(OS == "linux") or (OS == "android")', {
+ 'cflags_mozilla': [
+ '-Wall',
+ '-Wno-parentheses',
+ '-Wno-strict-prototypes',
+ '-Wmissing-prototypes',
+ '-Wno-format',
+ '-Wno-format-security',
+ ],
+ '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': [
+ ],
+ }],
+ ['have_ethtool_cmd_speed_hi==0', {
+ 'defines': [
+ "DONT_HAVE_ETHTOOL_SPEED_HI",
+ ]
+ }],
+ # libFuzzer instrumentation is not compatible with TSan.
+ # See also the comment in build/moz.configure/toolchain.configure.
+ ['(libfuzzer == 1) and (tsan == 0) and (libfuzzer_fuzzer_no_link_flag == 1)', {
+ 'cflags_mozilla': [
+ '-fsanitize=fuzzer-no-link'
+ ],
+ }],
+ ['(libfuzzer == 1) and (tsan == 0) and (libfuzzer_fuzzer_no_link_flag == 0)', {
+ 'cflags_mozilla': [
+ '-fsanitize-coverage=trace-pc-guard,trace-cmp'
+ ],
+ }],
+ ],
+ }]
+}
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.c b/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.c
new file mode 100644
index 0000000000..4c430b0e8b
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.c
@@ -0,0 +1,67 @@
+/*
+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 <nr_api.h>
+#include "nr_crypto.h"
+
+static int nr_ice_crypto_dummy_random_bytes(UCHAR *buf, size_t len)
+ {
+ fprintf(stderr,"Need to define crypto API implementation\n");
+
+ exit(1);
+ }
+
+static int nr_ice_crypto_dummy_hmac_sha1(UCHAR *key, size_t key_l, UCHAR *buf, size_t 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, size_t 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/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.h b/dom/media/webrtc/transport/third_party/nICEr/src/crypto/nr_crypto.h
new file mode 100644
index 0000000000..5fbbd8f097
--- /dev/null
+++ b/dom/media/webrtc/transport/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, size_t len);
+ int (*hmac_sha1)(UCHAR *key, size_t key_l, UCHAR *buf, size_t buf_l, UCHAR digest[20]);
+ int (*md5)(UCHAR *buf, size_t 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c
new file mode 100644
index 0000000000..b7cf2c9a99
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.c
@@ -0,0 +1,1052 @@
+/*
+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 <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)
+ {
+ *label = 0;
+ snprintf(label, size, "%s(%s|%s)", nr_ctype_name(cand->type),
+ cand->base.as_string, cand->stun_server->addr.as_string);
+
+ return (0);
+ }
+
+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(!(comp->stream->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) {
+ 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! */
+ if (done_cb) {
+ 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;
+
+ nr_ice_candidate_stop_gathering(cand);
+
+ switch(cand->type){
+ case HOST:
+ break;
+#ifdef USE_TURN
+ case RELAYED:
+ // record stats back to the ice ctx on destruction
+ if (cand->u.relayed.turn) {
+ nr_accumulate_count(&(cand->ctx->stats.turn_401s), cand->u.relayed.turn->cnt_401s);
+ nr_accumulate_count(&(cand->ctx->stats.turn_403s), cand->u.relayed.turn->cnt_403s);
+ nr_accumulate_count(&(cand->ctx->stats.turn_438s), cand->u.relayed.turn->cnt_438s);
+
+ nr_turn_stun_ctx* stun_ctx;
+ stun_ctx = STAILQ_FIRST(&cand->u.relayed.turn->stun_ctxs);
+ while (stun_ctx) {
+ nr_accumulate_count(&(cand->ctx->stats.stun_retransmits), stun_ctx->stun->retransmit_ct);
+
+ stun_ctx = STAILQ_NEXT(stun_ctx, entry);
+ }
+ }
+ 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;
+ }
+
+ RFREE(cand->mdns_addr);
+ RFREE(cand->foundation);
+ RFREE(cand->label);
+ RFREE(cand);
+
+ return(0);
+ }
+
+void nr_ice_candidate_stop_gathering(nr_ice_candidate *cand)
+ {
+ 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);
+ }
+
+ NR_async_timer_cancel(cand->delay_timer);
+ cand->delay_timer=0;
+ NR_async_timer_cancel(cand->ready_cb_timer);
+ cand->ready_cb_timer=0;
+
+ if(cand->resolver_handle){
+ nr_resolver_cancel(cand->ctx->resolver,cand->resolver_handle);
+ cand->resolver_handle=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;
+ // cast necessary because there is no guarantee that enum is signed.
+ // foundation->type should probably match nr_ice_candidate_type
+ if((int)cand->type != foundation->type)
+ goto next;
+ if(cand->type == SERVER_REFLEXIVE || cand->type == RELAYED) {
+ if(nr_transport_addr_cmp(&cand->stun_server->addr, &foundation->stun_server_addr, NR_TRANSPORT_ADDR_CMP_MODE_ADDR))
+ 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;
+ if(cand->type == SERVER_REFLEXIVE || cand->type == RELAYED) {
+ nr_transport_addr_copy(&foundation->stun_server_addr, &cand->stun_server->addr);
+ }
+ 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);
+ }
+
+static int nr_ice_candidate_use_nr_resolver(nr_transport_addr *addr)
+ {
+ if(addr->fqdn[0] == 0) {
+ return 0;
+ }
+
+ char use = 0;
+ if(addr->protocol == IPPROTO_UDP) {
+ NR_reg_get_char(NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP, &use);
+ } else if(addr->protocol == IPPROTO_TCP) {
+ NR_reg_get_char(NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP, &use);
+ } else {
+ assert(0);
+ }
+
+ return use;
+ }
+
+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 (nr_transport_addr_cmp(&cand->base, &cand->stun_server->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->addr.ip_version,
+ cand->stun_server->addr.protocol == IPPROTO_UDP ? "UDP"
+ : "TCP");
+ ABORT(R_NOT_FOUND); /* Same error code when DNS lookup fails */
+ }
+
+ if(nr_ice_candidate_use_nr_resolver(&cand->stun_server->addr)) {
+ r_log(
+ LOG_ICE, LOG_DEBUG,
+ "ICE-CANDIDATE(%s): Starting DNS resolution (%u/%s - %u/%s).",
+ cand->label, cand->base.ip_version,
+ cand->base.protocol == IPPROTO_UDP ? "UDP" : "TCP",
+ cand->stun_server->addr.ip_version,
+ cand->stun_server->addr.protocol == IPPROTO_UDP ? "UDP" : "TCP");
+ nr_resolver_resource resource;
+ int port;
+ resource.domain_name = cand->stun_server->addr.fqdn;
+ if (r = nr_transport_addr_get_port(&cand->stun_server->addr, &port)) {
+ ABORT(r);
+ }
+ resource.port = (uint16_t)port;
+ resource.stun_turn=protocol;
+ resource.transport_protocol = cand->stun_server->addr.protocol;
+
+ switch (cand->base.ip_version) {
+ case NR_IPV4:
+ resource.address_family=AF_INET;
+ break;
+ case NR_IPV6:
+ resource.address_family=AF_INET6;
+ break;
+ default:
+ assert(0);
+ 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);
+ }
+ } else {
+ /* No nr_resolver for this, just copy the address and finish init */
+ if (r = nr_transport_addr_copy(&cand->stun_server_addr,
+ &cand->stun_server->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);
+ }
+ 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);
+ }
+
+ if (nr_transport_addr_check_compatibility(addr, &cand->base)) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Skipping STUN server because of link local mis-match for 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->component->ctx,
+ &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 obfuscate_srflx_addr)
+ {
+ 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 (cand->mdns_addr) {
+ /* mdns_addr is NSID_LENGTH which is 39, - 2 for removing the "{" and "}"
+ + 6 for ".local" for a total of 43. */
+ strncpy(addr, cand->mdns_addr, sizeof(addr) - 1);
+ } else {
+ 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->flags &
+ (NR_ICE_CTX_FLAGS_RELAY_ONLY |
+ NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES)) ?
+ &cand->addr : &cand->base;
+
+ switch(cand->type){
+ case HOST:
+ break;
+ case SERVER_REFLEXIVE:
+ if (obfuscate_srflx_addr) {
+ snprintf(attr,maxlen," raddr 0.0.0.0 rport 0");
+ } else {
+ 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 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.h
new file mode 100644
index 0000000000..40e6545e37
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate.h
@@ -0,0 +1,124 @@
+/*
+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;
+ int trickled;
+#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 *mdns_addr; /* MDNS address, if any */
+ 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);
+void nr_ice_candidate_stop_gathering(nr_ice_candidate *cand);
+int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen, int obfuscate_srflx_addr);
+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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.c
new file mode 100644
index 0000000000..6a7233963d
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.c
@@ -0,0 +1,689 @@
+/*
+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 <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;
+
+ // record stats back to the ice ctx on destruction
+ if (pair->stun_client) {
+ nr_accumulate_count(&(pair->local->ctx->stats.stun_retransmits), pair->stun_client->retransmit_ct);
+ }
+
+ 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->local->stream->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:
+ if (_status) {
+ // cb doesn't return anything, but we should probably log that we aborted
+ // This also quiets the unused variable warnings.
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb pair addr = %s abort with status: %d",
+ pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string, _status);
+ }
+ 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);
+ nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy);
+ 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);
+ } else if (pair->local->stream->obsolete) {
+ r_log(LOG_ICE, LOG_DEBUG,
+ "ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for pair with "
+ "obsolete stream",
+ 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);
+ }
+
+void 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);
+ }
+ }
+
+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);
+ }
+
+void nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state)
+ {
+ 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){
+ nr_ice_component_failed_pair(pair->remote->component, pair);
+ }
+ }
+
+void nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, int log_level)
+ {
+ r_log(LOG_ICE,log_level,"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);
+ }
+
+
+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);
+ }
+
+void 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);
+ }
+
+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:
+ if (_status) {
+ // cb doesn't return anything, but we should probably log that we aborted
+ // This also quiets the unused variable warnings.
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s)/COMP(%d): STUN nominated cb pair as nominated: %s abort with status: %d",
+ pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string, _status);
+ }
+ 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.h
new file mode 100644
index 0000000000..49da1f7802
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_candidate_pair.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_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 */
+
+ // for RTCIceCandidatePairStats
+ UINT8 bytes_sent;
+ UINT8 bytes_recvd;
+ struct timeval last_sent;
+ struct timeval last_recvd;
+
+ 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);
+void nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state);
+void nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, int log_level);
+void 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);
+void 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_codeword.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_codeword.h
new file mode 100644
index 0000000000..76fce0b69b
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.c
new file mode 100644
index 0000000000..584a9466bc
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.c
@@ -0,0 +1,1786 @@
+/*
+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.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 void nr_ice_component_refresh_consent_cb(NR_SOCKET s, int how, void *cb_arg);
+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);
+int nr_ice_component_can_candidate_addr_pair(nr_transport_addr *local, nr_transport_addr *remote);
+int nr_ice_component_can_candidate_tcptype_pair(nr_socket_tcp_type left, nr_socket_tcp_type right);
+void nr_ice_component_consent_calc_consent_timer(nr_ice_component *comp);
+void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp);
+int nr_ice_component_refresh_consent(nr_stun_client_ctx *ctx, NR_async_cb finished_cb, void *cb_arg);
+int nr_ice_component_setup_consent(nr_ice_component *comp);
+int nr_ice_pre_answer_enqueue(nr_ice_component *comp, nr_socket *sock, nr_stun_server_request *req, int *dont_free);
+
+/* 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_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,&isock->stun_server))
+ 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);
+
+ /* Do this last; if this function fails, we should not be taking a reference to isock */
+ if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle))
+ 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-STREAM(%s): host address %s",component->stream->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-STREAM(%s): couldn't create socket for address %s",component->stream->label,addrs[i].addr.as_string);
+ continue;
+ }
+
+ if(r=nr_ice_socket_create(ctx,component,sock,NR_ICE_SOCKET_TYPE_DGRAM,&isock))
+ ABORT(r);
+
+ /* Create a STUN server context for this socket */
+ if ((r=nr_ice_component_create_stun_server_ctx(component,isock,&addrs[i].addr,lufrag,pwd)))
+ ABORT(r);
+
+ /* Make sure we don't leak this. Failures might result in it being
+ * unused, but we hand off references to this in enough places below
+ * that unwinding it all becomes impractical. */
+ STAILQ_INSERT_TAIL(&component->sockets,isock,entry);
+
+ if (!(component->stream->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<component->stream->stun_server_ct;j++){
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): Checking STUN server %s %s", component->stream->label, component->stream->stun_servers[j].addr.fqdn, component->stream->stun_servers[j].addr.as_string);
+ /* Skip non-UDP */
+ if (component->stream->stun_servers[j].addr.protocol != IPPROTO_UDP) continue;
+
+ if (nr_transport_addr_check_compatibility(
+ &addrs[i].addr, &component->stream->stun_servers[j].addr)) {
+ r_log(LOG_ICE,LOG_INFO,"ICE-STREAM(%s): Skipping STUN server because of address type mis-match",component->stream->label);
+ continue;
+ }
+
+ /* Ensure id is set (nr_ice_ctx_set_stun_servers does not) */
+ component->stream->stun_servers[j].id = j;
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock,sock,SERVER_REFLEXIVE,0,
+ &component->stream->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-STREAM(%s): relay only option results in no host candidate for %s",component->stream->label,addrs[i].addr.as_string);
+ }
+
+#ifdef USE_TURN
+ if ((component->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) &&
+ (component->stream->turn_server_ct == 0)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE-STREAM(%s): relay only option is set without any TURN server configured",component->stream->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<component->stream->turn_server_ct;j++){
+ nr_socket *turn_sock;
+ nr_ice_candidate *srvflx_cand=0;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): Checking TURN server %s %s", component->stream->label, component->stream->turn_servers[j].turn_server.addr.fqdn, component->stream->turn_servers[j].turn_server.addr.as_string);
+
+ /* Skip non-UDP */
+ if (component->stream->turn_servers[j].turn_server.addr.protocol != IPPROTO_UDP)
+ continue;
+
+ if (nr_transport_addr_check_compatibility(
+ &addrs[i].addr, &component->stream->turn_servers[j].turn_server.addr)) {
+ r_log(LOG_ICE,LOG_INFO,"ICE-STREAM(%s): Skipping TURN server because of address type mis-match",component->stream->label);
+ continue;
+ }
+
+ if (!(component->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) {
+ /* Ensure id is set with a unique value */
+ component->stream->turn_servers[j].turn_server.id = j + component->stream->stun_server_ct;
+ /* srvrflx */
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock,sock,SERVER_REFLEXIVE,0,
+ &component->stream->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(&turn_sock))
+ ABORT(r);
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock,turn_sock,RELAYED,0,
+ &component->stream->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=&component->stream->turn_servers[j];
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+
+ cand=0;
+ }
+#endif /* USE_TURN */
+ }
+
+ _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,component,&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,&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 ((component->stream->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) ||
+ (component->stream->flags & NR_ICE_CTX_FLAGS_ONLY_PROXY)) {
+ r_log(LOG_ICE,LOG_WARNING,"ICE-STREAM(%s): relay/proxy only option results in ICE TCP being disabled",component->stream->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-STREAM(%s): failed to create passive TCP host candidate: %d",component->stream->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-STREAM(%s): failed to create active TCP host candidate: %d",component->stream->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-STREAM(%s): failed to create simultanous open TCP host candidate: %d",component->stream->label,r);
+ }
+ }
+
+ /* And srvrflx candidates for each STUN server */
+ for(j=0;j<component->stream->stun_server_ct;j++){
+ if (component->stream->stun_servers[j].addr.protocol != IPPROTO_TCP) continue;
+
+ if (isock_psv) {
+ if(r=nr_ice_candidate_create(ctx,component,
+ isock_psv,isock_psv->sock,SERVER_REFLEXIVE,TCP_TYPE_PASSIVE,
+ &component->stream->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,
+ &component->stream->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<component->stream->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;
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): Checking TURN server %s %s", component->stream->label, component->stream->turn_servers[j].turn_server.addr.fqdn, component->stream->turn_servers[j].turn_server.addr.as_string);
+
+ /* Skip non-TCP */
+ if (component->stream->turn_servers[j].turn_server.addr.protocol != IPPROTO_TCP)
+ continue;
+
+ /* Create relay candidate */
+ if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr)))
+ ABORT(r);
+ addr.protocol = IPPROTO_TCP;
+
+ if (nr_transport_addr_check_compatibility(
+ &addr, &component->stream->turn_servers[j].turn_server.addr)) {
+ r_log(LOG_ICE,LOG_INFO,"ICE-STREAM(%s): Skipping TURN server because of address type mis-match",component->stream->label);
+ 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,
+ &component->stream->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,
+ &component->stream->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 (component->stream->turn_servers[j].turn_server.addr.fqdn[0] != 0) {
+ /* If we're going to use TLS, make sure that's recorded */
+ addr.tls = component->stream->turn_servers[j].turn_server.addr.tls;
+ }
+
+ if ((r=nr_transport_addr_fmt_addr_string(&addr)))
+ ABORT(r);
+
+ r_log(LOG_ICE, LOG_DEBUG,
+ "ICE-STREAM(%s): Creating socket for address %s (turn server %s)",
+ component->stream->label, addr.as_string,
+ component->stream->turn_servers[j].turn_server.addr.as_string);
+
+ /* Create a local socket */
+ if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addr,&local_sock))){
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): couldn't create socket for address %s",component->stream->label,addr.as_string);
+ continue;
+ }
+
+ r_log(LOG_ICE,LOG_DEBUG,"nr_ice_component_initialize_tcp creating TURN TCP wrappers");
+
+ /* 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(&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);
+
+
+ /* Create a STUN server context for this socket */
+ if ((r=nr_ice_component_create_stun_server_ctx(component,turn_isock,&addr,lufrag,pwd)))
+ ABORT(r);
+
+ /* Make sure we don't leak this. Failures might result in it being
+ * unused, but we hand off references to this in enough places below
+ * that unwinding it all becomes impractical. */
+ STAILQ_INSERT_TAIL(&component->sockets,turn_isock,entry);
+
+ /* Attach ourselves to it */
+ if(r=nr_ice_candidate_create(ctx,component,
+ turn_isock,turn_sock,RELAYED,TCP_TYPE_NONE,
+ &component->stream->turn_servers[j].turn_server,component->component_id,&cand))
+ ABORT(r);
+ cand->u.relayed.srvflx_candidate=NULL;
+ cand->u.relayed.server=&component->stream->turn_servers[j];
+ TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp);
+ component->candidate_ct++;
+ cand=0;
+ }
+#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;
+ assert(lufrag);
+ if (!lufrag)
+ ABORT(R_INTERNAL);
+ lpwd=component->stream->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);
+ }
+
+void nr_ice_component_stop_gathering(nr_ice_component *component)
+ {
+ nr_ice_candidate *c1,*c2;
+ TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){
+ nr_ice_candidate_stop_gathering(c1);
+ }
+ }
+
+int nr_ice_component_is_done_gathering(nr_ice_component *comp)
+ {
+ nr_ice_candidate *cand=TAILQ_FIRST(&comp->candidates);
+ while(cand){
+ if(cand->state != NR_ICE_CAND_STATE_INITIALIZED &&
+ cand->state != NR_ICE_CAND_STATE_FAILED){
+ return 0;
+ }
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+ return 1;
+ }
+
+
+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_DISABLE_HOST_CANDIDATES) &&
+ !(ctx->flags & NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES) &&
+ ((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(_status);
+ }
+
+/* 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;
+ 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 *pcomp=cb_arg;
+ nr_transport_addr local_addr;
+ int r,_status;
+
+ if(pcomp->state==NR_ICE_COMPONENT_FAILED) {
+ *error=400;
+ ABORT(R_REJECTED);
+ }
+
+ if (pcomp->local_component->stream->obsolete) {
+ /* Don't do any triggered check stuff in thiis case. */
+ return 0;
+ }
+
+ /* 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(pcomp, &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(local->protocol != remote->protocol)
+ 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;
+
+ /* https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-03#section-3.3.2 */
+ if(lcand->type == RELAYED && pcand->mdns_addr && strlen(pcand->mdns_addr)) {
+ 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. */
+ nr_ice_media_stream_component_failed(comp->stream, comp);
+ }
+
+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);
+ }
+
+
+void nr_ice_component_disconnected(nr_ice_component *comp)
+ {
+ if (!comp->can_send) {
+ return;
+ }
+
+ if (comp->disconnected) {
+ return;
+ }
+
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): component disconnected",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ comp->disconnected = 1;
+
+ /* a single disconnected component disconnects the stream */
+ nr_ice_media_stream_set_disconnected(comp->stream, NR_ICE_MEDIA_STREAM_DISCONNECTED);
+ }
+
+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);
+
+ comp->disconnected = 0;
+
+ nr_ice_media_stream_check_if_connected(comp->stream);
+
+ 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_disconnected(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;
+
+ if (!comp->consent_ctx) {
+ return;
+ }
+
+ if (comp->consent_timer) {
+ NR_async_timer_cancel(comp->consent_timer);
+ }
+ 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);
+ }
+
+ 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_refresh_consent_now(nr_ice_component *comp)
+ {
+ nr_ice_component_consent_timer_cb(0, 0, comp);
+ }
+
+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);
+ comp->consent_ctx = 0;
+ }
+ }
+
+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);
+
+ nr_ice_component_consent_destroy(comp);
+
+ 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;
+ comp->disconnected = 0;
+ 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);
+
+ nr_ice_candidate_pair_cancel(pair->pctx,p2,0);
+ }
+
+ 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);
+
+ nr_ice_candidate_pair_cancel(pair->pctx,p2,0);
+ }
+
+ 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);
+
+ nr_ice_media_stream_component_nominated(comp->stream,comp);
+
+ _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);
+ }
+
+void nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair)
+ {
+ nr_ice_component_check_if_failed(comp);
+ }
+
+void 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);
+ nr_ice_media_stream_component_failed(comp->stream,comp);
+ }
+ }
+ }
+
+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 _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);
+ }
+
+ /* We do not throw an error after this, because we've inserted the pair. */
+ nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,pair);
+
+ /* 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);
+ }
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ nr_ice_candidate_pair_destroy(&pair);
+ }
+ 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);
+
+ }
+
+
+void nr_ice_component_dump_state(nr_ice_component *comp, int log_level)
+ {
+ nr_ice_candidate *cand;
+
+ if (comp->local_component) {
+ r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s): Remote component %d in state %d - dumping candidates",comp->ctx->label,comp->stream->label,comp->component_id,comp->state);
+ } else {
+ r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s): Local component %d - dumping candidates",comp->ctx->label,comp->stream->label,comp->component_id);
+ }
+
+ cand=TAILQ_FIRST(&comp->candidates);
+ while(cand){
+ r_log(LOG_ICE,log_level,"ICE(%s)/ICE-STREAM(%s)/CAND(%s): %s",comp->ctx->label,comp->stream->label,cand->codeword,cand->label);
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+ }
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.h
new file mode 100644
index 0000000000..0b12a68d58
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_component.h
@@ -0,0 +1,111 @@
+/*
+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;
+ int disconnected;
+ 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);
+void nr_ice_component_stop_gathering(nr_ice_component *component);
+int nr_ice_component_is_done_gathering(nr_ice_component *comp);
+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);
+void nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
+void 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);
+void nr_ice_component_consent_destroy(nr_ice_component *comp);
+void nr_ice_component_refresh_consent_now(nr_ice_component *comp);
+void nr_ice_component_disconnected(nr_ice_component *comp);
+void nr_ice_component_dump_state(nr_ice_component *comp, int log_level);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c
new file mode 100644
index 0000000000..0d498845a4
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.c
@@ -0,0 +1,1125 @@
+/*
+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 <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 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].addr))
+ ABORT(r);
+ 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_cfg){
+ RFREE(ctx->stun_servers_cfg);
+ ctx->stun_servers_cfg=NULL;
+ ctx->stun_server_ct_cfg=0;
+ }
+
+ if (ct) {
+ if(!(ctx->stun_servers_cfg=RCALLOC(sizeof(nr_ice_stun_server)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ memcpy(ctx->stun_servers_cfg,servers,sizeof(nr_ice_stun_server)*ct);
+ ctx->stun_server_ct_cfg = 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_cfg){
+ for (int i = 0; i < ctx->turn_server_ct_cfg; i++) {
+ RFREE(ctx->turn_servers_cfg[i].username);
+ r_data_destroy(&ctx->turn_servers_cfg[i].password);
+ }
+ RFREE(ctx->turn_servers_cfg);
+ ctx->turn_servers_cfg=NULL;
+ ctx->turn_server_ct_cfg=0;
+ }
+
+ if(ct) {
+ if(!(ctx->turn_servers_cfg=RCALLOC(sizeof(nr_ice_turn_server)*ct)))
+ ABORT(R_NO_MEMORY);
+
+ memcpy(ctx->turn_servers_cfg,servers,sizeof(nr_ice_turn_server)*ct);
+ ctx->turn_server_ct_cfg = 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_cfg[i].username = r_strdup(servers[i].username))) {
+ ABORT(R_NO_MEMORY);
+ }
+ if (r = r_data_create(&ctx->turn_servers_cfg[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);
+ }
+
+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.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)
+ {
+ 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);
+
+ /* Get the STUN servers */
+ if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX,
+ (unsigned int *)&ctx->stun_server_ct_cfg)||ctx->stun_server_ct_cfg==0) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No STUN servers specified in nICEr registry", ctx->label);
+ ctx->stun_server_ct_cfg=0;
+ }
+
+ /* 31 is the max for our priority algorithm */
+ if(ctx->stun_server_ct_cfg>31){
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN servers specified: max=31", ctx->label);
+ ctx->stun_server_ct_cfg=31;
+ }
+
+ if(ctx->stun_server_ct_cfg>0){
+ if(r=nr_ice_fetch_stun_servers(ctx->stun_server_ct_cfg,&ctx->stun_servers_cfg)){
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load STUN servers from registry", ctx->label);
+ ctx->stun_server_ct_cfg=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_cfg)||ctx->turn_server_ct_cfg==0) {
+ r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No TURN servers specified in nICEr registry", ctx->label);
+ ctx->turn_server_ct_cfg=0;
+ }
+#else
+ ctx->turn_server_ct_cfg=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_cfg+ctx->turn_server_ct_cfg)>31){
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN/TURN servers specified: max=31", ctx->label);
+ ctx->turn_server_ct_cfg=31-ctx->stun_server_ct_cfg;
+ }
+
+#ifdef USE_TURN
+ if(ctx->turn_server_ct_cfg>0){
+ if(r=nr_ice_fetch_turn_servers(ctx->turn_server_ct_cfg,&ctx->turn_servers_cfg)){
+ ctx->turn_server_ct_cfg=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);
+ }
+ }
+
+ ctx->target_for_default_local_address_lookup=0;
+
+ 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(&ctx);
+
+ return(_status);
+ }
+
+ 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;
+ }
+
+ void nr_ice_ctx_destroy(nr_ice_ctx** ctxp) {
+ if (!ctxp || !*ctxp) return;
+
+ nr_ice_ctx* ctx = *ctxp;
+ nr_ice_foundation *f1,*f2;
+ nr_ice_media_stream *s1,*s2;
+ int i;
+ nr_ice_stun_id *id1,*id2;
+
+ ctx->done_cb = 0;
+ ctx->trickle_cb = 0;
+
+ 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_cfg);
+
+ RFREE(ctx->local_addrs);
+
+ RFREE(ctx->target_for_default_local_address_lookup);
+
+ for (i = 0; i < ctx->turn_server_ct_cfg; i++) {
+ RFREE(ctx->turn_servers_cfg[i].username);
+ r_data_destroy(&ctx->turn_servers_cfg[i].password);
+ }
+ RFREE(ctx->turn_servers_cfg);
+
+ f1=STAILQ_FIRST(&ctx->foundations);
+ while(f1){
+ f2=STAILQ_NEXT(f1,entry);
+ RFREE(f1);
+ f1=f2;
+ }
+
+ 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_factory_destroy(&ctx->socket_factory);
+
+ RFREE(ctx);
+
+ *ctxp=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;
+ nr_ice_media_stream *stream;
+ int component_id;
+
+ assert(cb_arg);
+ if (!cb_arg)
+ return;
+ ctx = cand->ctx;
+ stream = cand->stream;
+ component_id = cand->component_id;
+
+ ctx->uninitialized_candidates--;
+ if (cand->state == NR_ICE_CAND_STATE_FAILED) {
+ r_log(LOG_ICE, LOG_WARNING,
+ "ICE(%s)/CAND(%s): failed to initialize, %d remaining", ctx->label,
+ cand->label, ctx->uninitialized_candidates);
+ } else {
+ r_log(LOG_ICE, LOG_DEBUG, "ICE(%s)/CAND(%s): initialized, %d remaining",
+ ctx->label, cand->label, 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 (was_pruned) {
+ cand = NULL;
+ }
+
+ /* If we are initialized, the candidate wasn't pruned,
+ and we have a trickle ICE callback fire the callback */
+ if (ctx->trickle_cb && cand &&
+ !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 (nr_ice_media_stream_is_done_gathering(stream) &&
+ ctx->trickle_cb) {
+ ctx->trickle_cb(ctx->trickle_cb_arg, ctx, stream, component_id, NULL);
+ }
+
+ if(ctx->uninitialized_candidates==0){
+ r_log(LOG_ICE, LOG_INFO, "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_INFO,
+ "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 creating a UDP socket, binding it to a wildcard
+ address, and connecting it to the remote IP. Because this is UDP, no packets
+ are sent. This lets us query the local address assigned to the socket by the
+ kernel.
+
+ If the context's remote address is NULL, then the application wasn't loaded
+ over the network, and we can fall back on connecting to a known public
+ address (namely Google's):
+
+ IPv4: 8.8.8.8
+ IPv6: 2001:4860:4860::8888
+*/
+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, known_remote_addr;
+ nr_transport_addr *remote_addr=ctx->target_for_default_local_address_lookup;
+ 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 (!remote_addr || nr_transport_addr_is_loopback(remote_addr)) {
+ if ((r=nr_str_port_to_transport_addr("8.8.8.8", 53, IPPROTO_UDP, &known_remote_addr)))
+ ABORT(r);
+ remote_addr=&known_remote_addr;
+ }
+ break;
+ case NR_IPV6:
+ if ((r=nr_str_port_to_transport_addr("::0", 0, IPPROTO_UDP, &addr)))
+ ABORT(r);
+ if (!remote_addr || nr_transport_addr_is_loopback(remote_addr)) {
+ if ((r=nr_str_port_to_transport_addr("2001:4860:4860::8888", 53, IPPROTO_UDP, &known_remote_addr)))
+ ABORT(r);
+ remote_addr=&known_remote_addr;
+ }
+ 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 default addr is found in local addrs, copy the more fully
+ // complete local addr to the output arg. Don't need to worry
+ // about comparing ports here.
+ 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 default addr is not in local addrs, just copy the transport addr
+ // to output arg.
+ if (i == addr_ct) {
+ if ((r=nr_transport_addr_copy(&addrp->addr, &default_addr)))
+ ABORT(r);
+ (void)strlcpy(addrp->addr.ifname, "default route", sizeof(addrp->addr.ifname));
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+/* if handed a IPv4 default_local_addr, looks for IPv6 address on same interface
+ if handed a IPv6 default_local_addr, looks for IPv4 address on same interface
+*/
+static int nr_ice_get_assoc_interface_address(nr_local_addr* default_local_addr,
+ nr_local_addr* local_addrs, int addr_ct,
+ nr_local_addr* assoc_addrp)
+ {
+ int r, _status;
+ int i, ip_version;
+
+ if (!default_local_addr || !local_addrs || !addr_ct) {
+ ABORT(R_BAD_ARGS);
+ }
+
+ /* set _status to R_EOD in case we don't find an associated address */
+ _status = R_EOD;
+
+ /* look for IPv6 if we have IPv4, look for IPv4 if we have IPv6 */
+ ip_version = (NR_IPV4 == default_local_addr->addr.ip_version?NR_IPV6:NR_IPV4);
+
+ for (i=0; i<addr_ct; ++i) {
+ /* if we find the ip_version we're looking for on the matching interface,
+ copy it to assoc_addrp.
+ */
+ if (local_addrs[i].addr.ip_version == ip_version &&
+ !strcmp(local_addrs[i].addr.ifname, default_local_addr->addr.ifname)) {
+ if (r=nr_local_addr_copy(assoc_addrp, &local_addrs[i])) {
+ ABORT(r);
+ }
+ _status = 0;
+ break;
+ }
+ }
+
+ abort:
+ return(_status);
+ }
+
+int nr_ice_set_local_addresses(nr_ice_ctx *ctx,
+ nr_local_addr* stun_addrs, int stun_addr_ct)
+ {
+ 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 (!stun_addrs || !stun_addr_ct) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): no stun addrs provided",ctx->label);
+ ABORT(R_BAD_ARGS);
+ }
+
+ addr_ct = MIN(stun_addr_ct, MAXADDRS);
+ r_log(LOG_ICE, LOG_DEBUG, "ICE(%s): copy %d pre-fetched stun addrs", ctx->label, addr_ct);
+ for (i=0; i<addr_ct; ++i) {
+ if (r=nr_local_addr_copy(&local_addrs[i], &stun_addrs[i])) {
+ ABORT(r);
+ }
+ }
+
+ // removes duplicates and, based on prefs, loopback and link_local addrs
+ if (r=nr_stun_filter_local_addresses(local_addrs, &addr_ct)) {
+ ABORT(r);
+ }
+
+ 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;
+ }
+
+ r_log(LOG_ICE, LOG_DEBUG,
+ "ICE(%s): use only default local addresses: %s\n",
+ ctx->label,
+ (char*)(ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS?"yes":"no"));
+ if ((!addr_ct) || (ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS)) {
+ if (ctx->target_for_default_local_address_lookup) {
+ /* Get just the default IPv4 or IPv6 addr */
+ if(!nr_ice_get_default_local_address(
+ ctx, ctx->target_for_default_local_address_lookup->ip_version,
+ local_addrs, addr_ct, &default_addrs[default_addr_ct])) {
+ nr_local_addr *new_addr = &default_addrs[default_addr_ct];
+
+ ++default_addr_ct;
+
+ /* If we have a default target address, check for an associated
+ address on the same interface. For example, if the default
+ target address is IPv6, this will find an associated IPv4
+ address on the same interface.
+ This makes ICE w/ dual stacks work better - Bug 1609124.
+ */
+ if(!nr_ice_get_assoc_interface_address(
+ new_addr, local_addrs, addr_ct,
+ &default_addrs[default_addr_ct])) {
+ ++default_addr_ct;
+ }
+ }
+ } else {
+ /* 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!=R_ALREADY)) {
+ 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_set_target_for_default_local_address_lookup(nr_ice_ctx *ctx, const char *target_ip, UINT2 target_port)
+ {
+ int r,_status;
+
+ if (ctx->target_for_default_local_address_lookup) {
+ RFREE(ctx->target_for_default_local_address_lookup);
+ ctx->target_for_default_local_address_lookup=0;
+ }
+
+ if (!(ctx->target_for_default_local_address_lookup=RCALLOC(sizeof(nr_transport_addr))))
+ ABORT(R_NO_MEMORY);
+
+ if ((r=nr_str_port_to_transport_addr(target_ip, target_port, IPPROTO_UDP, ctx->target_for_default_local_address_lookup))) {
+ RFREE(ctx->target_for_default_local_address_lookup);
+ ctx->target_for_default_local_address_lookup=0;
+ 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;
+ nr_local_addr stun_addrs[MAXADDRS];
+ int stun_addr_ct;
+
+ if (!ctx->local_addrs) {
+ if((r=nr_stun_find_local_addresses(stun_addrs,MAXADDRS,&stun_addr_ct))) {
+ ABORT(r);
+ }
+ if((r=nr_ice_set_local_addresses(ctx,stun_addrs,stun_addr_ct))) {
+ 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(!stream->obsolete) {
+ if(r=nr_ice_media_stream_initialize(ctx,stream)) {
+ r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to initialize a stream; this might not be an unrecoverable error, if this stream will be marked obsolete soon due to an ICE restart.",ctx->label);
+ }
+ }
+
+ 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,const char *label,const char *ufrag,const char *pwd,int components, nr_ice_media_stream **streamp)
+ {
+ int r,_status;
+
+ if(r=nr_ice_media_stream_create(ctx,label,ufrag,pwd,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)
+ {
+ *attrctp=0;
+ *attrsp=0;
+ return(0);
+ }
+
+static int nr_ice_random_string(char *str, int len)
+ {
+ unsigned char bytes[100];
+ size_t 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_DISABLE_HOST_CANDIDATES) {
+ if (cand->type == HOST)
+ return 1;
+ }
+
+ if (cand->stream->obsolete) {
+ 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h
new file mode 100644
index 0000000000..8b3081f567
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_ctx.h
@@ -0,0 +1,188 @@
+/*
+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"
+
+typedef struct nr_ice_foundation_ {
+ int index;
+
+ nr_transport_addr addr;
+ int type;
+ /* ICE spec says that we only compare IP address, not port */
+ nr_transport_addr stun_server_addr;
+
+ 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;
+
+typedef struct nr_ice_stats_ {
+ UINT2 stun_retransmits;
+ UINT2 turn_401s;
+ UINT2 turn_403s;
+ UINT2 turn_438s;
+} nr_ice_stats;
+
+struct nr_ice_ctx_ {
+ UINT4 flags;
+ char *label;
+
+ UINT4 Ta;
+
+ nr_ice_stun_server *stun_servers_cfg; /* The list of stun servers */
+ int stun_server_ct_cfg;
+ nr_ice_turn_server *turn_servers_cfg; /* The list of turn servers */
+ int turn_server_ct_cfg;
+ 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_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];
+ nr_ice_stats stats;
+
+ nr_transport_addr *target_for_default_local_address_lookup;
+};
+
+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_AGGRESSIVE_NOMINATION (1)
+#define NR_ICE_CTX_FLAGS_LITE (1<<1)
+#define NR_ICE_CTX_FLAGS_RELAY_ONLY (1<<2)
+#define NR_ICE_CTX_FLAGS_DISABLE_HOST_CANDIDATES (1<<3)
+#define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS (1<<4)
+#define NR_ICE_CTX_FLAGS_ONLY_PROXY (1<<5)
+#define NR_ICE_CTX_FLAGS_OBFUSCATE_HOST_ADDRESSES (1<<6)
+
+void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags);
+void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags);
+void nr_ice_ctx_destroy(nr_ice_ctx** ctxp);
+int nr_ice_set_local_addresses(nr_ice_ctx *ctx, nr_local_addr* stun_addrs, int stun_addr_ct);
+int nr_ice_set_target_for_default_local_address_lookup(nr_ice_ctx *ctx, const char *target_ip, UINT2 target_port);
+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,const char *label,const char *ufrag,const char *pwd,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);
+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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_handler.h
new file mode 100644
index 0000000000..5a0690adad
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c
new file mode 100644
index 0000000000..62bfbad629
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.c
@@ -0,0 +1,1087 @@
+/*
+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.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","CONNECTED","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,const char *label,const char *ufrag,const char *pwd,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);
+
+ if(!(stream->ufrag=r_strdup(ufrag)))
+ ABORT(R_NO_MEMORY);
+
+ if(!(stream->pwd=r_strdup(pwd)))
+ 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->disconnected = 0;
+ stream->component_ct=components;
+ stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED;
+ stream->obsolete = 0;
+ stream->r2l_user = 0;
+ stream->l2r_user = 0;
+ stream->flags = ctx->flags;
+ if(ctx->stun_server_ct_cfg) {
+ if(!(stream->stun_servers=RCALLOC(sizeof(nr_ice_stun_server)*(ctx->stun_server_ct_cfg))))
+ ABORT(R_NO_MEMORY);
+
+ memcpy(stream->stun_servers,ctx->stun_servers_cfg,sizeof(nr_ice_stun_server)*(ctx->stun_server_ct_cfg));
+ stream->stun_server_ct = ctx->stun_server_ct_cfg;
+ }
+
+ if(ctx->turn_server_ct_cfg) {
+ if(!(stream->turn_servers=RCALLOC(sizeof(nr_ice_turn_server)*(ctx->turn_server_ct_cfg))))
+ ABORT(R_NO_MEMORY);
+
+ for(int i = 0; i < ctx->turn_server_ct_cfg; ++i) {
+ nr_ice_turn_server *dst = &stream->turn_servers[i];
+ nr_ice_turn_server *src = &ctx->turn_servers_cfg[i];
+ memcpy(&dst->turn_server, &src->turn_server, sizeof(nr_ice_stun_server));
+ dst->username = r_strdup(src->username);
+ r_data_create(&dst->password, src->password->data, src->password->len);
+ }
+ stream->turn_server_ct = ctx->turn_server_ct_cfg;
+ }
+
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-STREAM(%s): flags %d",stream->label,stream->flags);
+ *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);
+
+ RFREE(stream->stun_servers);
+ for (int i = 0; i < stream->turn_server_ct; i++) {
+ RFREE(stream->turn_servers[i].username);
+ r_data_destroy(&stream->turn_servers[i].password);
+ }
+ RFREE(stream->turn_servers);
+
+ 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;
+
+ assert(!stream->obsolete);
+
+ 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=2;
+ nr_ice_component *comp;
+ char **attrs=0;
+ int index=0;
+ nr_ice_candidate *cand;
+ int r,_status;
+ char *tmp=0;
+
+ *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);
+ }
+
+ /* 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, 0))
+ ABORT(r);
+
+ index++;
+ }
+
+ cand=TAILQ_NEXT(cand,entry_comp);
+ }
+ }
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ /* Now, ufrag and pwd */
+ if(!(tmp=RMALLOC(100)))
+ ABORT(R_NO_MEMORY);
+ snprintf(tmp,100,"ice-ufrag:%s",stream->ufrag);
+ attrs[index++]=tmp;
+
+ if(!(tmp=RMALLOC(100)))
+ ABORT(R_NO_MEMORY);
+ snprintf(tmp,100,"ice-pwd:%s",stream->pwd);
+ attrs[index++]=tmp;
+
+ *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_CONNECTED) {
+ 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_INFO,"ICE-PEER(%s): no FROZEN/WAITING pairs for %s",stream->pctx->label,stream->label);
+ }
+
+ _status=0;
+ abort:
+ if (_status) {
+ // cb doesn't return anything, but we should probably log that we aborted
+ // This also quiets the unused variable warnings.
+ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer cb for media stream %s abort with status: %d",
+ stream->pctx->label,stream->label, _status);
+ }
+ 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);
+ }
+
+ if (stream->local_stream->obsolete) {
+ 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_CONNECTED) {
+ 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;
+
+ /* 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);
+ }
+
+ /* Now go through the check lists for the other streams */
+ str=STAILQ_FIRST(&stream->pctx->peer_streams);
+ while(str){
+ if(str!=stream && !str->local_stream->obsolete){
+ 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);
+ }
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+
+void nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int log_level)
+ {
+ nr_ice_cand_pair *pair;
+ nr_ice_component *comp;
+
+ if(stream->local_stream){
+ /* stream has a corresponding local_stream */
+ nr_ice_media_stream_dump_state(stream->local_stream->pctx,stream->local_stream, log_level);
+ r_log(LOG_ICE,log_level,"ICE-PEER(%s)/STREAM(%s): state dump", stream->pctx->label, stream->label);
+ } else {
+ r_log(LOG_ICE,log_level,"ICE(%s)/STREAM(%s): state dump", stream->ctx->label, stream->label);
+ }
+
+ pair=TAILQ_FIRST(&stream->check_list);
+ while(pair){
+ nr_ice_candidate_pair_dump_state(pair, log_level);
+ pair=TAILQ_NEXT(pair,check_queue_entry);
+ }
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ nr_ice_component_dump_state(comp, log_level);
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ }
+
+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((size_t)state < sizeof(nr_ice_media_stream_states)/sizeof(char *));
+ assert((size_t)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;
+ if (state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) {
+ nr_ice_media_stream_dump_state(str->pctx,str,LOG_ERR);
+ }
+
+ return(0);
+ }
+
+void nr_ice_media_stream_stop_checking(nr_ice_media_stream *str)
+ {
+ nr_ice_cand_pair *p;
+ nr_ice_component *comp;
+
+ /* Cancel candidate pairs */
+ p=TAILQ_FIRST(&str->check_list);
+ while(p){
+ nr_ice_candidate_pair_cancel(p->pctx,p,0);
+ p=TAILQ_NEXT(p,check_queue_entry);
+ }
+
+ if(str->timer) {
+ NR_async_timer_cancel(str->timer);
+ str->timer = 0;
+ }
+
+ /* Cancel consent timers in case it is running already */
+ comp=STAILQ_FIRST(&str->components);
+ while(comp){
+ nr_ice_component_consent_destroy(comp);
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ }
+
+void nr_ice_media_stream_set_obsolete(nr_ice_media_stream *str)
+ {
+ nr_ice_component *c1,*c2;
+ str->obsolete = 1;
+
+ STAILQ_FOREACH_SAFE(c1, &str->components, entry, c2){
+ nr_ice_component_stop_gathering(c1);
+ }
+
+ nr_ice_media_stream_stop_checking(str);
+ }
+
+int nr_ice_media_stream_is_done_gathering(nr_ice_media_stream *str)
+ {
+ nr_ice_component *comp;
+ comp=STAILQ_FIRST(&str->components);
+ while(comp){
+ if(!nr_ice_component_is_done_gathering(comp)) {
+ return 0;
+ }
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ return 1;
+ }
+
+void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream)
+ {
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if(comp->disconnected) {
+ nr_ice_component_refresh_consent_now(comp);
+ }
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ }
+
+void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream)
+ {
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ comp->disconnected = 1;
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ }
+
+void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected)
+ {
+ if (stream->disconnected == disconnected) {
+ return;
+ }
+
+ if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) {
+ return;
+ }
+ stream->disconnected = disconnected;
+
+ if (disconnected == NR_ICE_MEDIA_STREAM_DISCONNECTED) {
+ if (!stream->local_stream->obsolete) {
+ nr_ice_peer_ctx_disconnected(stream->pctx);
+ }
+ } else {
+ nr_ice_peer_ctx_check_if_connected(stream->pctx);
+ }
+ }
+
+int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream)
+ {
+ 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->disconnected)
+ break;
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ /* At least one disconnected component */
+ if(comp)
+ goto done;
+
+ nr_ice_media_stream_set_disconnected(stream, NR_ICE_MEDIA_STREAM_CONNECTED);
+
+ done:
+ return(0);
+ }
+
+/* S OK, this component has a nominated. If every component has a nominated,
+ the stream is ready */
+void nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component)
+ {
+ 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)
+ return;
+
+ /* 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_CONNECTED);
+
+ /* Cancel our timer */
+ if(stream->timer){
+ NR_async_timer_cancel(stream->timer);
+ stream->timer=0;
+ }
+
+ if (stream->pctx->handler && !stream->local_stream->obsolete) {
+ stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream);
+ }
+
+ /* Now tell the peer_ctx that we're connected */
+ nr_ice_peer_ctx_check_if_connected(stream->pctx);
+ }
+
+void nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component)
+ {
+ 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);
+
+ nr_ice_media_stream_stop_checking(stream);
+
+ if (stream->pctx->handler && !stream->local_stream->obsolete) {
+ stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream);
+ }
+
+ /* Now tell the peer_ctx that we've failed */
+ nr_ice_peer_ctx_check_if_connected(stream->pctx);
+ }
+
+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)) {
+ if ((r==R_IO_ERROR) || (r==R_EOD)) {
+ nr_ice_component_disconnected(comp);
+ }
+ ABORT(r);
+ }
+
+ // accumulate the sent bytes for the active candidate pair
+ comp->active->bytes_sent += len;
+ gettimeofday(&comp->active->last_sent, 0);
+
+ _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;
+
+ /* 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h
new file mode 100644
index 0000000000..99f906c100
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_media_stream.h
@@ -0,0 +1,146 @@
+/*
+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 */
+
+#include "transport_addr.h"
+
+typedef struct nr_ice_stun_server_ {
+ nr_transport_addr addr;
+ int id;
+} nr_ice_stun_server;
+
+typedef struct nr_ice_turn_server_ {
+ nr_ice_stun_server turn_server;
+ char *username;
+ Data *password;
+} nr_ice_turn_server;
+
+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;
+ /* The stream is being replaced by another, so it will not continue any ICE
+ * processing. If this stream is connected already, traffic can continue to
+ * flow for a limited time while the new stream gets ready. */
+ int obsolete;
+
+#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_CONNECTED 4
+#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5
+
+ int disconnected;
+
+#define NR_ICE_MEDIA_STREAM_CONNECTED 0
+#define NR_ICE_MEDIA_STREAM_DISCONNECTED 1
+
+ /* Copy of flags field from nr_ice_ctx */
+ int flags;
+
+ /* Copy of STUN/TURN servers from nr_ice_ctx */
+ 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_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,const char *label,const char *ufrag,const char *pwd,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);
+void nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int log_level);
+void nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component);
+void nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component);
+void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream);
+void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream);
+void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected);
+int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream);
+int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
+void nr_ice_media_stream_stop_checking(nr_ice_media_stream *str);
+void nr_ice_media_stream_set_obsolete(nr_ice_media_stream *str);
+int nr_ice_media_stream_is_done_gathering(nr_ice_media_stream *str);
+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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_parser.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_parser.c
new file mode 100644
index 0000000000..25cda3364a
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_parser.c
@@ -0,0 +1,564 @@
+/*
+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 <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);
+
+ RFREE(stream->ufrag);
+ 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);
+
+ RFREE(stream->pwd);
+ 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;
+ pctx->controlling = 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);
+ }
+ 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);
+ }
+ 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c
new file mode 100644
index 0000000000..0bf97eb984
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.c
@@ -0,0 +1,875 @@
+/*
+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.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_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 trickled, const char *mdns_addr);
+static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx);
+
+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 {
+ pctx->controlling=1;
+ }
+ 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(&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;
+
+ nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct);
+
+ /* Now that we have the ufrag and password, compute all the username/password
+ pairs */
+ lufrag=stream->ufrag;
+ lpwd=stream->pwd;
+ assert(lufrag);
+ assert(lpwd);
+ rufrag=pstream->ufrag;
+ rpwd=pstream->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);
+ pstream=0;
+
+ _status=0;
+ abort:
+ if (_status) {
+ nr_ice_media_stream_destroy(&pstream);
+ }
+ return(_status);
+ }
+
+static void 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],0,0)) {
+ 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 */
+ }
+
+static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate, int trickled, const char *mdns_addr)
+ {
+ 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);
+
+ /* set the trickled flag on the candidate */
+ cand->trickled = trickled;
+
+ if (mdns_addr) {
+ cand->mdns_addr = r_strdup(mdns_addr);
+ if (!cand->mdns_addr) {
+ ABORT(R_NO_MEMORY);
+ }
+ }
+
+ /* 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){
+ /* Very common for the answerer when it uses rtcp-mux */
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) no such component for candidate %s",pctx->ctx->label,pctx->label, candidate);
+ ABORT(R_REJECTED);
+ }
+
+ if (comp->state == NR_ICE_COMPONENT_DISABLED) {
+ r_log(LOG_ICE,LOG_WARNING,"Peer offered candidate for disabled remote component: %s", candidate);
+ ABORT(R_BAD_DATA);
+ }
+ if (comp->local_component->state == NR_ICE_COMPONENT_DISABLED) {
+ r_log(LOG_ICE,LOG_WARNING,"Peer offered candidate for disabled local component: %s", candidate);
+ 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);
+
+ if(!pstream) {
+ /* No peer streams at all, presumably because they do not exist yet.
+ * Don't log a warning here. */
+ ABORT(R_NOT_FOUND);
+ }
+
+ 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, const char *mdns_addr)
+ {
+ nr_ice_media_stream *pstream;
+ int r,_status;
+ int needs_pairing = 0;
+
+ if (stream->obsolete) {
+ return 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,1,mdns_addr)){
+ 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) {
+ /* Start the remote trickle grace timeout if it hasn't been started by
+ another trickled candidate or from the SDP. */
+ if (!pctx->trickle_grace_period_timer) {
+ nr_ice_peer_ctx_start_trickle_timer(pctx);
+ }
+
+ 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) {
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) starting grace period timer for %u ms",pctx->ctx->label,pctx->label, 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;
+
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ while(stream){
+ if(!stream->local_stream->obsolete) {
+ 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);
+
+ /* Start the remote trickle grace timeout if it hasn't been started
+ already. */
+ if (!pctx->trickle_grace_period_timer) {
+ nr_ice_peer_ctx_start_trickle_timer(pctx);
+ }
+
+ 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);
+ }
+
+ void nr_ice_peer_ctx_destroy(nr_ice_peer_ctx** pctxp) {
+ if (!pctxp || !*pctxp) return;
+
+ nr_ice_peer_ctx* pctx = *pctxp;
+ nr_ice_media_stream *str1,*str2;
+
+ /* Stop calling the handler */
+ pctx->handler = 0;
+
+ NR_async_timer_cancel(pctx->connected_cb_timer);
+ RFREE(pctx->label);
+
+ 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);
+
+ *pctxp=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;
+
+ /* Ensure that grace period timer is running. We might cancel this if we
+ * didn't actually start any pairs. */
+ nr_ice_peer_ctx_start_trickle_timer(pctx);
+
+ /* 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;
+
+ nr_ice_peer_ctx_check_if_connected(pctx);
+
+ 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) {
+ if(!stream->local_stream->obsolete) {
+ 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 && pctx->ctx->uninitialized_candidates) {
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) no checks to start, but gathering is not done yet, cancelling grace period timer",pctx->ctx->label,pctx->label);
+ /* Never mind on the grace period timer */
+ NR_async_timer_cancel(pctx->trickle_grace_period_timer);
+ pctx->trickle_grace_period_timer=0;
+ 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);
+ }
+ }
+ }
+
+void nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx, int log_level)
+ {
+ nr_ice_media_stream *stream;
+
+ r_log(LOG_ICE,log_level,"PEER %s STATE DUMP",pctx->label);
+ r_log(LOG_ICE,log_level,"==========================================");
+ stream=STAILQ_FIRST(&pctx->peer_streams);
+ while(stream){
+ nr_ice_media_stream_dump_state(pctx,stream,log_level);
+ }
+ r_log(LOG_ICE,log_level,"==========================================");
+ }
+
+void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *str;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): refreshing consent on all streams",pctx->label);
+
+ str=STAILQ_FIRST(&pctx->peer_streams);
+ while(str) {
+ nr_ice_media_stream_refresh_consent_all(str);
+ str=STAILQ_NEXT(str,entry);
+ }
+ }
+
+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);
+
+ pctx->reported_connected = 0;
+ }
+ }
+
+void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *str;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): disconnecting all streams",pctx->label);
+
+ str=STAILQ_FIRST(&pctx->peer_streams);
+ while(str) {
+ nr_ice_media_stream_disconnect_all_components(str);
+
+ /* The first stream to be disconnected will cause the peer ctx to signal
+ the disconnect up. */
+ nr_ice_media_stream_set_disconnected(str, NR_ICE_MEDIA_STREAM_DISCONNECTED);
+
+ str=STAILQ_NEXT(str,entry);
+ }
+ }
+
+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 */
+void nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *str;
+ int failed=0;
+ int succeeded=0;
+
+ str=STAILQ_FIRST(&pctx->peer_streams);
+ while(str){
+ if (!str->local_stream->obsolete){
+ if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED){
+ succeeded++;
+ }
+ else if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FAILED){
+ failed++;
+ }
+ else{
+ break;
+ }
+ }
+ str=STAILQ_NEXT(str,entry);
+ }
+
+ if(str)
+ return; /* 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);
+
+ /* Make sure grace period timer is cancelled */
+ if(pctx->trickle_grace_period_timer) {
+ r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) cancelling grace period timer",pctx->ctx->label,pctx->label);
+ NR_async_timer_cancel(pctx->trickle_grace_period_timer);
+ pctx->trickle_grace_period_timer=0;
+ }
+
+ /* 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);
+ }
+ }
+
+
+/* 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);
+
+ // accumulate the received bytes for the active candidate pair
+ if (peer_comp->active) {
+ peer_comp->active->bytes_recvd += len;
+ gettimeofday(&peer_comp->active->last_recvd, 0);
+ }
+
+ /* 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_peer_ctx.h
new file mode 100644
index 0000000000..ec73d96a03
--- /dev/null
+++ b/dom/media/webrtc/transport/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;
+
+ 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);
+void 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, const char *mdns_addr);
+
+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_refresh_consent_all_streams(nr_ice_peer_ctx *pctx);
+void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx);
+void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx);
+void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx);
+void nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx, int log_level);
+int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx);
+void 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/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_reg.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_reg.h
new file mode 100644
index 0000000000..3acc02360a
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_reg.h
@@ -0,0 +1,81 @@
+/*
+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"
+#define NR_ICE_REG_USE_NR_RESOLVER_FOR_TCP "ice.tcp.use_nr_resolver"
+#define NR_ICE_REG_USE_NR_RESOLVER_FOR_UDP "ice.udp.use_nr_resolver"
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.c b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.c
new file mode 100644
index 0000000000..a6c8513300
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.c
@@ -0,0 +1,404 @@
+/*
+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 <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);
+
+ 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);
+ nr_ice_socket_failed(sock);
+ return;
+ }
+ }
+
+ 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) {
+ 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);
+ }
+
+void nr_ice_socket_failed(nr_ice_socket *sock)
+ {
+ nr_ice_stun_ctx *s1,*s2;
+ TAILQ_FOREACH_SAFE(s1, &sock->stun_ctxs, entry, s2){
+ switch (s1->type) {
+ case NR_ICE_STUN_NONE:
+ break;
+ case NR_ICE_STUN_CLIENT:
+ nr_stun_client_failed(s1->u.client);
+ break;
+ case NR_ICE_STUN_SERVER:
+ /* Nothing to do here? */
+ break;
+#ifdef USE_TURN
+ case NR_ICE_TURN_CLIENT:
+ nr_turn_client_failed(s1->u.turn_client.turn_client);
+ break;
+#endif
+ default:
+ assert(0);
+ }
+ }
+ }
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.h b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.h
new file mode 100644
index 0000000000..9b73a2690e
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/ice/ice_socket.h
@@ -0,0 +1,98 @@
+/*
+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);
+void nr_ice_socket_failed(nr_ice_socket *sock);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.c
new file mode 100644
index 0000000000..a0896d5e9d
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.c
@@ -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.
+*/
+
+#include <nr_api.h>
+#include <string.h>
+#include "local_addr.h"
+
+int nr_local_addr_copy(nr_local_addr *to, nr_local_addr *from)
+ {
+ int r,_status;
+
+ if (r=nr_transport_addr_copy(&(to->addr), &(from->addr))) {
+ ABORT(r);
+ }
+ to->interface = from->interface;
+ to->flags = from->flags;
+
+ _status=0;
+ abort:
+ return(_status);
+ }
+
+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 %s",
+ vpn, type, addr->interface.estimated_speed,
+ (addr->flags & NR_ADDR_FLAG_TEMPORARY ? "temporary" : ""));
+ buf[len - 1] = '\0';
+ return (0);
+ }
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.h
new file mode 100644
index 0000000000..fb963e1115
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/local_addr.h
@@ -0,0 +1,62 @@
+/*
+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 0
+#define NR_INTERFACE_TYPE_WIRED 1
+#define NR_INTERFACE_TYPE_WIFI 1 << 1
+#define NR_INTERFACE_TYPE_MOBILE 1 << 2
+#define NR_INTERFACE_TYPE_VPN 1 << 3
+#define NR_INTERFACE_TYPE_TEREDO 1 << 4
+ int estimated_speed; /* Speed in kbps */
+} nr_interface;
+
+typedef struct nr_local_addr_ {
+ nr_transport_addr addr;
+ nr_interface interface;
+#define NR_ADDR_FLAG_TEMPORARY 0x1
+ int flags;
+} 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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.c
new file mode 100644
index 0000000000..75e5f95467
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_interface_prioritizer.h
new file mode 100644
index 0000000000..c8a36526cc
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.c
new file mode 100644
index 0000000000..4dbf1bbe91
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_resolver.h
new file mode 100644
index 0000000000..376ba9998b
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.c
new file mode 100644
index 0000000000..c9867610a6
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.c
@@ -0,0 +1,187 @@
+/*
+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 <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,
+ const 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, const 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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.h
new file mode 100644
index 0000000000..777837f6cc
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket.h
@@ -0,0 +1,123 @@
+/*
+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)
+/* Undef before defining to avoid a compiler warning */
+#undef restrict
+#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,
+ const 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, const 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, const 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, const 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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_local.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_local.h
new file mode 100644
index 0000000000..a2f813ff66
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c
new file mode 100644
index 0000000000..9b2489b214
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.c
@@ -0,0 +1,642 @@
+/*
+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,
+ const 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, const 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, const 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->addr.protocol != IPPROTO_TCP) {
+ r_log(LOG_ICE, LOG_INFO,
+ "%s:%d function %s skipping UDP STUN server(addr:%s)", __FILE__,
+ __LINE__, __FUNCTION__, stun_server->addr.as_string);
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (nr_transport_addr_cmp(&stun_server->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->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);
+
+ nr_transport_addr stun_server_addr;
+
+ nr_transport_addr_copy(&stun_server_addr, &stun_server->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,
+ struct nr_ice_component_ *component,
+ 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) {
+ nr_ice_stun_server *stun_servers;
+ nr_ice_turn_server *turn_servers;
+ int stun_server_ct, turn_server_ct;
+ if (component) {
+ stun_servers = component->stream->stun_servers;
+ turn_servers = component->stream->turn_servers;
+ stun_server_ct = component->stream->stun_server_ct;
+ turn_server_ct = component->stream->turn_server_ct;
+ } else {
+ /* Mainly for unit-testing */
+ stun_servers = ctx->stun_servers_cfg;
+ turn_servers = ctx->turn_servers_cfg;
+ stun_server_ct = ctx->stun_server_ct_cfg;
+ turn_server_ct = ctx->turn_server_ct_cfg;
+ }
+ if (stun_servers) {
+ for (i=0; i<stun_server_ct; ++i) {
+ if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock,
+ 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 (turn_servers) {
+ for (i=0; i<turn_server_ct; ++i) {
+ if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock,
+ &(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,
+ const 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;
+
+ 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,
+ const 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, const 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, const 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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.h
new file mode 100644
index 0000000000..8413e67293
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_multi_tcp.h
@@ -0,0 +1,53 @@
+/*
+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,
+ struct nr_ice_component_ *component,
+ 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,
+ const nr_transport_addr *addr);
+
+#endif
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.c
new file mode 100644
index 0000000000..0c9ec56744
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/nr_socket_wrapper.h
new file mode 100644
index 0000000000..717518e23e
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.c
new file mode 100644
index 0000000000..efedb3782a
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.c
@@ -0,0 +1,559 @@
+/*
+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 <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:
+ if (addr->tls) {
+ protocol = "TLS";
+ } else {
+ 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;
+ /* leave room for a fully-expanded IPV4-mapped IPV6 address */
+ char buffer[46];
+
+ switch(addr->ip_version){
+ case NR_IPV4:
+ if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer))) {
+ strncpy(buffer, "[error]", sizeof(buffer));
+ }
+ break;
+ case NR_IPV6:
+ if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer))) {
+ strncpy(buffer, "[error]", sizeof(buffer));
+ }
+ break;
+ default:
+ ABORT(R_INTERNAL);
+ }
+ buffer[sizeof(buffer) - 1] = '\0';
+
+ snprintf(buf,len,"%s:%s",addr->ifname,buffer);
+ buf[len - 1] = '\0';
+
+ _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));
+ }
+ else if(saddr->sa_family==AF_INET6){
+ addr->ip_version=NR_IPV6;
+
+ memcpy(&addr->u.addr6, saddr, 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, const nr_transport_addr *from)
+ {
+ memcpy(to,from,sizeof(nr_transport_addr));
+ return 0;
+ }
+
+int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, const 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;
+ }
+
+int nr_transport_addr_copy_addrport(nr_transport_addr *to, const nr_transport_addr *from)
+ {
+ int r,_status;
+
+ switch (from->ip_version) {
+ case NR_IPV4:
+ memcpy(&to->u.addr4, &from->u.addr4, sizeof(to->u.addr4));
+ break;
+ case NR_IPV6:
+ memcpy(&to->u.addr6, &from->u.addr6, sizeof(to->u.addr6));
+ break;
+ default:
+ ABORT(R_BAD_ARGS);
+ }
+
+ to->ip_version = from->ip_version;
+
+ 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);
+
+ 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));
+
+ 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;
+
+ if (addr->fqdn[0]) {
+ strncpy(str, addr->fqdn, maxlen);
+ } else {
+ 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(const 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(const nr_transport_addr *addr1,const 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);
+
+ 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(const 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:
+ NR_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:
+ NR_UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_is_link_local(const 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:
+ NR_UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_is_mac_based(const nr_transport_addr *addr)
+ {
+ switch(addr->ip_version){
+ case NR_IPV4:
+ // IPv4 has no MAC based self assigned IP addresses
+ return(0);
+ case NR_IPV6:
+ {
+ // RFC 2373, Appendix A: lower 64bit 0x020000FFFE000000
+ // indicates a MAC based IPv6 address
+ UINT4* macCom = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr + 8);
+ UINT4* macExt = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr + 12);
+ if ((*macCom & htonl(0x020000FF)) == htonl(0x020000FF) &&
+ (*macExt & htonl(0xFF000000)) == htonl(0xFE000000)) {
+ return(1);
+ }
+ }
+ break;
+ default:
+ NR_UNIMPLEMENTED;
+ }
+ return(0);
+ }
+
+int nr_transport_addr_is_teredo(const nr_transport_addr *addr)
+ {
+ switch(addr->ip_version){
+ case NR_IPV4:
+ return(0);
+ case NR_IPV6:
+ {
+ UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr);
+ if ((*addrTop & htonl(0xFFFFFFFF)) == htonl(0x20010000))
+ return(1);
+ }
+ break;
+ default:
+ NR_UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_check_compatibility(const nr_transport_addr *addr1, const nr_transport_addr *addr2)
+ {
+ // first make sure we're comparing the same ip versions and protocols
+ if ((addr1->ip_version != addr2->ip_version) ||
+ (addr1->protocol != addr2->protocol)) {
+ return(1);
+ }
+
+ if (!addr1->fqdn[0] && !addr2->fqdn[0]) {
+ // now make sure the link local status matches
+ if (nr_transport_addr_is_link_local(addr1) !=
+ nr_transport_addr_is_link_local(addr2)) {
+ return(1);
+ }
+ }
+ return(0);
+ }
+
+int nr_transport_addr_is_wildcard(const 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:
+ NR_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(const nr_transport_addr *addr)
+ {
+ switch(addr->ip_version){
+ case NR_IPV4:
+ {
+ UINT4 ip = ntohl(addr->u.addr4.sin_addr.s_addr);
+ for (size_t 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:
+ NR_UNIMPLEMENTED;
+ }
+
+ return(0);
+ }
+
+int nr_transport_addr_is_reliable_transport(const nr_transport_addr *addr)
+ {
+ return addr->protocol == IPPROTO_TCP;
+ }
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.h
new file mode 100644
index 0000000000..c75c70a1d9
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr.h
@@ -0,0 +1,112 @@
+/*
+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 <stdbool.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#include "r_types.h"
+
+/* 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 */
+ 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];
+ char fqdn[256];
+ bool is_proxied;
+ bool tls;
+} 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(const nr_transport_addr *addr, int *port);
+int nr_transport_addr_cmp(const nr_transport_addr *addr1,const 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(const nr_transport_addr *addr);
+int nr_transport_addr_is_loopback(const nr_transport_addr *addr);
+int nr_transport_addr_get_private_addr_range(const nr_transport_addr *addr);
+int nr_transport_addr_is_link_local(const nr_transport_addr *addr);
+int nr_transport_addr_is_mac_based(const nr_transport_addr *addr);
+int nr_transport_addr_is_teredo(const nr_transport_addr *addr);
+int nr_transport_addr_check_compatibility(const nr_transport_addr *addr1, const nr_transport_addr *addr2);
+int nr_transport_addr_copy(nr_transport_addr *to, const nr_transport_addr *from);
+int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, const nr_transport_addr *from);
+/* Copies _just_ the address and port (also handles IP version) */
+int nr_transport_addr_copy_addrport(nr_transport_addr *to, const 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(const nr_transport_addr *addr);
+
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.c b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.c
new file mode 100644
index 0000000000..10f93f1947
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.c
@@ -0,0 +1,230 @@
+/*
+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 <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) {
+ (void)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:
+ NR_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/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.h b/dom/media/webrtc/transport/third_party/nICEr/src/net/transport_addr_reg.h
new file mode 100644
index 0000000000..761953a9ce
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c
new file mode 100644
index 0000000000..ec2d084445
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c
@@ -0,0 +1,110 @@
+/* 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 defined(BSD) || defined(DARWIN)
+#include "addrs-bsd.h"
+#include <csi_platform.h>
+#include <assert.h>
+#include <string.h>
+#include "util.h"
+#include "stun_util.h"
+#include "util.h"
+#include <r_macros.h>
+
+#include <sys/types.h> /* getifaddrs */
+#include <ifaddrs.h> /* getifaddrs */
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+
+static int
+stun_ifaddr_get_v6_flags(struct ifaddrs *ifaddr)
+{
+ if (ifaddr->ifa_addr->sa_family != AF_INET6) {
+ return 0;
+ }
+
+ int flags = 0;
+ int s = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (!s) {
+ r_log(NR_LOG_STUN, LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed, errno=%d", errno);
+ assert(0);
+ return 0;
+ }
+ struct in6_ifreq ifr6;
+ memset(&ifr6, 0, sizeof(ifr6));
+ strncpy(ifr6.ifr_name, ifaddr->ifa_name, sizeof(ifr6.ifr_name));
+ /* ifr_addr is a sockaddr_in6, ifa_addr is a sockaddr* */
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifaddr->ifa_addr;
+ ifr6.ifr_addr = *sin6;
+ if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) != -1) {
+ flags = ifr6.ifr_ifru.ifru_flags6;
+ } else {
+ r_log(NR_LOG_STUN, LOG_ERR, "ioctl(SIOCGIFAFLAG_IN6) failed, errno=%d", errno);
+ assert(0);
+ }
+ close(s);
+ return flags;
+}
+
+static int
+stun_ifaddr_is_disallowed_v6(int flags) {
+ return flags & (IN6_IFF_ANYCAST | IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED);
+}
+
+int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count)
+{
+ int r,_status,flags;
+ 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:
+ flags = stun_ifaddr_get_v6_flags(if_addr);
+ if (!stun_ifaddr_is_disallowed_v6(flags)) {
+ 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 (flags & IN6_IFF_TEMPORARY) {
+ addrs[*count].flags |= NR_ADDR_FLAG_TEMPORARY;
+ }
+ (void)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
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h
new file mode 100644
index 0000000000..b575586f48
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h
@@ -0,0 +1,13 @@
+/* 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_IFADDRS_BSD_H_
+#define STUN_IFADDRS_BSD_H_
+
+#include "local_addr.h"
+
+int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count);
+
+#endif /* STUN_IFADDRS_BSD_H_ */
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c
new file mode 100644
index 0000000000..73e85c6ccc
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c
@@ -0,0 +1,285 @@
+/*
+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(LINUX)
+#include "addrs-netlink.h"
+#include <csi_platform.h>
+#include <assert.h>
+#include <string.h>
+#include "util.h"
+#include "stun_util.h"
+#include "util.h"
+#include <r_macros.h>
+
+#include <stdlib.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>
+
+#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 */
+
+
+struct netlinkrequest {
+ struct nlmsghdr header;
+ struct ifaddrmsg msg;
+};
+
+static const int kMaxReadSize = 4096;
+
+static void set_ifname(nr_local_addr *addr, struct ifaddrmsg* msg) {
+ assert(sizeof(addr->addr.ifname) > IF_NAMESIZE);
+ if_indextoname(msg->ifa_index, addr->addr.ifname);
+}
+
+static int get_siocgifflags(nr_local_addr *addr) {
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1) {
+ assert(0);
+ return 0;
+ }
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, addr->addr.ifname, IFNAMSIZ - 1);
+ int rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
+ close(fd);
+ if (rc == -1) {
+ assert(0);
+ return 0;
+ }
+ return ifr.ifr_flags;
+}
+
+static int set_sockaddr(nr_local_addr *addr, struct ifaddrmsg* msg, struct rtattr* rta) {
+ assert(rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL);
+ void *data = RTA_DATA(rta);
+ size_t len = RTA_PAYLOAD(rta);
+ if (msg->ifa_family == AF_INET) {
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(struct sockaddr_in));
+ sa.sin_family = AF_INET;
+ memcpy(&sa.sin_addr, data, len);
+ return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr));
+ } else if (msg->ifa_family == AF_INET6) {
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(struct sockaddr_in6));
+ sa.sin6_family = AF_INET6;
+ /* We do not set sin6_scope_id to ifa_index, because that is only valid for
+ * link local addresses, and we don't use those anyway */
+ memcpy(&sa.sin6_addr, data, len);
+ return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr));
+ }
+
+ return R_BAD_ARGS;
+}
+
+static int
+stun_ifaddr_is_disallowed_v6(int flags) {
+ return flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | IFA_F_DEPRECATED);
+}
+
+static int
+stun_convert_netlink(nr_local_addr *addr, struct ifaddrmsg *address_msg, struct rtattr* rta)
+{
+ int r = set_sockaddr(addr, address_msg, rta);
+ if (r) {
+ r_log(NR_LOG_STUN, LOG_ERR, "set_sockaddr error r = %d", r);
+ return r;
+ }
+
+ set_ifname(addr, address_msg);
+
+ if (address_msg->ifa_flags & IFA_F_TEMPORARY) {
+ addr->flags |= NR_ADDR_FLAG_TEMPORARY;
+ }
+
+ int flags = get_siocgifflags(addr);
+ if (flags & IFF_POINTOPOINT)
+ {
+ addr->interface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN;
+ /* TODO (Bug 896913): find backend network type of this VPN */
+ }
+
+#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, addr->addr.ifname, 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 */
+ addr->interface.type = NR_INTERFACE_TYPE_WIRED;
+#ifdef DONT_HAVE_ETHTOOL_SPEED_HI
+ addr->interface.estimated_speed = ecmd.speed;
+#else
+ addr->interface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000;
+#endif
+ }
+
+ strncpy(wrq.ifr_name, addr->addr.ifname, sizeof(wrq.ifr_name));
+ e = ioctl(s, SIOCGIWRATE, &wrq);
+ if (e == 0)
+ {
+ addr->interface.type = NR_INTERFACE_TYPE_WIFI;
+ addr->interface.estimated_speed = wrq.u.bitrate.value / 1000;
+ }
+
+ close(s);
+
+#else
+ addr->interface.type = NR_INTERFACE_TYPE_UNKNOWN;
+ addr->interface.estimated_speed = 0;
+#endif
+ return 0;
+}
+
+int
+stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count)
+{
+ int _status;
+ int fd = 0;
+
+ /* Scope everything else since we're using ABORT. */
+ {
+ *count = 0;
+
+ if (maxaddrs <= 0)
+ ABORT(R_BAD_ARGS);
+
+ fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (fd < 0) {
+ ABORT(R_INTERNAL);
+ }
+
+ 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 bytes = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0);
+ if ((size_t)bytes != ifaddr_request.header.nlmsg_len) {
+ ABORT(R_INTERNAL);
+ }
+
+ char buf[kMaxReadSize];
+ ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0);
+ while ((amount_read > 0) && (*count != maxaddrs)) {
+ struct nlmsghdr* header = (struct nlmsghdr*)&buf[0];
+ size_t header_size = (size_t)amount_read;
+ for ( ; NLMSG_OK(header, header_size) && (*count != maxaddrs);
+ header = NLMSG_NEXT(header, header_size)) {
+ switch (header->nlmsg_type) {
+ case NLMSG_DONE:
+ /* Success. Return. */
+ close(fd);
+ return 0;
+ case NLMSG_ERROR:
+ ABORT(R_INTERNAL);
+ 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);
+ bool found = false;
+ while (RTA_OK(rta, payload_len)) {
+ // This is a bit convoluted. IFA_ADDRESS and IFA_LOCAL are the
+ // same thing except when using a POINTTOPOINT interface, in
+ // which case IFA_LOCAL is the local address, and IFA_ADDRESS is
+ // the remote address. In a reasonable world, that would mean we
+ // could just use IFA_LOCAL all the time. Sadly, IFA_LOCAL is not
+ // always set (IPv6 in particular). So, we have to be on the
+ // lookout for both, and prefer IFA_LOCAL when present.
+ if (rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL) {
+ int family = address_msg->ifa_family;
+ if ((family == AF_INET || family == AF_INET6) &&
+ !stun_ifaddr_is_disallowed_v6(address_msg->ifa_flags) &&
+ !stun_convert_netlink(&addrs[*count], address_msg, rta)) {
+ found = true;
+ if (rta->rta_type == IFA_LOCAL) {
+ // IFA_LOCAL is what we really want; if we find it we're
+ // done. If this is IFA_ADDRESS instead, we do not proceed
+ // yet, and allow a subsequent IFA_LOCAL to overwrite what
+ // we just put in |addrs|.
+ break;
+ }
+ }
+ }
+ /* TODO: Use IFA_LABEL instead of if_indextoname? We would need
+ * to remember how many nr_local_addr we've converted for this
+ * ifaddrmsg, and set the label on all of them. */
+ rta = RTA_NEXT(rta, payload_len);
+ }
+
+ if (found) {
+ ++(*count);
+ }
+ break;
+ }
+ }
+ }
+ amount_read = recv(fd, &buf, kMaxReadSize, 0);
+ }
+ }
+
+ _status=0;
+abort:
+ close(fd);
+ return(_status);
+}
+
+#endif /* defined(LINUX) */
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h
new file mode 100644
index 0000000000..b2b07bddc9
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h
@@ -0,0 +1,45 @@
+/*
+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_NETLINK_H_
+#define WEBRTC_BASE_IFADDRS_NETLINK_H_
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include "local_addr.h"
+
+/* for platforms with netlink (android and linux) */
+/* Filters out things like deprecated addresses, and stuff that doesn't pass
+ * IPv6 duplicate address detection */
+int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count);
+#endif /* WEBRTC_BASE_IFADDRS_NETLINK_H_ */
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c
new file mode 100644
index 0000000000..4fdf5e5d44
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c
@@ -0,0 +1,210 @@
+/* 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/. */
+
+#ifdef WIN32
+
+#include "addrs-win32.h"
+#include <csi_platform.h>
+#include <assert.h>
+#include <string.h>
+#include "util.h"
+#include "stun_util.h"
+#include "util.h"
+#include <r_macros.h>
+#include "nr_crypto.h"
+
+#include <winsock2.h>
+#include <iphlpapi.h>
+#include <tchar.h>
+
+#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_win32_address_disallowed(IP_ADAPTER_UNICAST_ADDRESS *addr)
+{
+ return (addr->DadState != NldsPreferred) &&
+ (addr->DadState != IpDadStatePreferred);
+}
+
+static int stun_win32_address_temp_v6(IP_ADAPTER_UNICAST_ADDRESS *addr)
+{
+ return (addr->Address.lpSockaddr->sa_family == AF_INET6) &&
+ (addr->SuffixOrigin == IpSuffixOriginRandom);
+}
+
+int
+stun_getaddrs_filtered(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)) {
+ r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized sa_family for address on adapter %lu", tmpAddress->IfIndex);
+ continue;
+ }
+
+ if (stun_win32_address_disallowed(u)) {
+ continue;
+ }
+
+ if ((r=nr_sockaddr_to_transport_addr((struct sockaddr*)sa_addr->lpSockaddr, IPPROTO_UDP, 0, &(addrs[n].addr)))) {
+ ABORT(r);
+ }
+
+ 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 (stun_win32_address_temp_v6(u)) {
+ addrs[n].flags |= NR_ADDR_FLAG_TEMPORARY;
+ }
+
+ if (++n >= maxaddrs)
+ goto done;
+ }
+ }
+ }
+
+ done:
+ *count = n;
+ _status = 0;
+
+ abort:
+ RFREE(AdapterAddresses);
+ return _status;
+}
+
+#endif //WIN32
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h
new file mode 100644
index 0000000000..a00802192a
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h
@@ -0,0 +1,13 @@
+/* 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_IFADDRS_WIN32_H_
+#define STUN_IFADDRS_WIN32_H_
+
+#include "local_addr.h"
+
+int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count);
+
+#endif /* STUN_IFADDRS_WIN32_H_ */
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c
new file mode 100644
index 0000000000..362b7d828e
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c
@@ -0,0 +1,176 @@
+/*
+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 <csi_platform.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef WIN32
+#include "addrs-win32.h"
+#elif defined(BSD) || defined(DARWIN)
+#include "addrs-bsd.h"
+#else
+#include "addrs-netlink.h"
+#endif
+
+#include "stun.h"
+#include "addrs.h"
+#include "util.h"
+
+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_filter_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;
+ /* We prefer temp ipv6 for their privacy properties. If we cannot get
+ * that, we prefer ipv6 that are not based on mac address. */
+ int filter_mac_ipv6 = 0;
+ int filter_teredo_ipv6 = 0;
+ int filter_non_temp_ipv6 = 0;
+
+ tmp = RMALLOC(*count * sizeof(*tmp));
+ if (!tmp)
+ ABORT(R_NO_MEMORY);
+
+ for (i = 0; i < *count; ++i) {
+ if (addrs[i].addr.ip_version == NR_IPV6) {
+ if (nr_transport_addr_is_teredo(&addrs[i].addr)) {
+ addrs[i].interface.type |= NR_INTERFACE_TYPE_TEREDO;
+ /* Prefer teredo over mac-based address. Probably will never see
+ * both. */
+ filter_mac_ipv6 = 1;
+ } else {
+ filter_teredo_ipv6 = 1;
+ }
+
+ if (!nr_transport_addr_is_mac_based(&addrs[i].addr)) {
+ filter_mac_ipv6 = 1;
+ }
+
+ if (addrs[i].flags & NR_ADDR_FLAG_TEMPORARY) {
+ filter_non_temp_ipv6 = 1;
+ }
+ }
+ }
+
+ 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 &&
+ nr_transport_addr_is_link_local(&addrs[i].addr)) {
+ /* skip addrs[i], it's a link-local address */
+ }
+ else if (filter_mac_ipv6 &&
+ nr_transport_addr_is_mac_based(&addrs[i].addr)) {
+ /* skip addrs[i], it's MAC based */
+ }
+ else if (filter_teredo_ipv6 &&
+ nr_transport_addr_is_teredo(&addrs[i].addr)) {
+ /* skip addrs[i], it's a Teredo address */
+ }
+ else if (filter_non_temp_ipv6 &&
+ (addrs[i].addr.ip_version == NR_IPV6) &&
+ !(addrs[i].flags & NR_ADDR_FLAG_TEMPORARY)) {
+ /* skip addrs[i], it's a non-temporary ipv6, and we have a temporary */
+ }
+ 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 *count)
+{
+ int _status=0;
+ int i;
+ char typestr[100];
+
+ // Ensure output records are always fully defined. See bug 1589990.
+ if (maxaddrs > 0) {
+ memset(addrs, 0, maxaddrs * sizeof(nr_local_addr));
+ }
+
+ _status = stun_getaddrs_filtered(addrs, maxaddrs, count);
+
+ 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);
+ }
+
+ return _status;
+}
+
+#endif
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h
new file mode 100644
index 0000000000..522bd92fd5
--- /dev/null
+++ b/dom/media/webrtc/transport/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 *count);
+int nr_stun_filter_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count);
+
+#endif
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
new file mode 100644
index 0000000000..4b5b92fb75
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
@@ -0,0 +1,656 @@
+/*
+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, const 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, const 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_TCP, &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_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
+ }
+
+ 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, const 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);
+
+ 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, const 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);
+ /* Report this failure up; the only way to do this is a readable callback.
+ * Once the user tries to read (using nr_socket_buffered_stun_recvfrom), it
+ * will notice that there has been a failure. */
+ if (sock->readable_cb) {
+ sock->readable_cb(s, NR_ASYNC_WAIT_READ, sock->readable_cb_arg);
+ }
+ } else if (sock->pending) {
+ nr_socket_buffered_stun_arm_writable_cb(sock);
+ }
+}
+
+int nr_socket_buffered_stun_reset(nr_socket* sock_arg, nr_socket* new_inner) {
+ int r, _status;
+ NR_SOCKET fd;
+
+ nr_socket_buffered_stun* sock = (nr_socket_buffered_stun*)sock_arg->obj;
+
+ if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) {
+ r_log(LOG_GENERIC, LOG_DEBUG, "In %s, canceling wait on old socket", __FUNCTION__);
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE);
+ NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ);
+ }
+
+ nr_socket_destroy(&sock->inner);
+ sock->inner = new_inner;
+
+ sock->read_state = NR_ICE_SOCKET_READ_NONE;
+ sock->connected = 0;
+
+ sock->bytes_read = 0;
+ sock->bytes_needed = (sock->framing_type == ICE_TCP_FRAMING)
+ ? sizeof(nr_frame_header)
+ : sizeof(nr_stun_message_header);
+ sock->pending = 0;
+
+ nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes);
+ nr_p_buf_ctx_destroy(&sock->p_bufs);
+
+ STAILQ_INIT(&sock->pending_writes);
+
+ if ((r = nr_p_buf_ctx_create(sock->buffer_size, &sock->p_bufs))) {
+ ABORT(r);
+ }
+
+ if ((r = nr_ip4_port_to_transport_addr(INADDR_ANY, 0, IPPROTO_TCP,
+ &sock->remote_addr))) {
+ ABORT(r);
+ }
+
+ _status = 0;
+abort:
+ return (_status);
+}
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h
new file mode 100644
index 0000000000..c635ee393d
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.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 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);
+
+int nr_socket_buffered_stun_reset(nr_socket *sock, nr_socket *new_inner);
+
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c
new file mode 100644
index 0000000000..1a0162b13e
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c
@@ -0,0 +1,195 @@
+/*
+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 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, const 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 **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, const 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h
new file mode 100644
index 0000000000..13506045a8
--- /dev/null
+++ b/dom/media/webrtc/transport/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 **sockp);
+int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx);
+
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h
new file mode 100644
index 0000000000..a32751d795
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h
@@ -0,0 +1,218 @@
+/*
+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>
+#ifdef DARWIN
+#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
+/* from an expired draft defined as optional, but in the required range */
+#define NR_STUN_ATTR_BANDWIDTH 0x0010
+#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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c
new file mode 100644
index 0000000000..001b38e7c4
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c
@@ -0,0 +1,611 @@
+/*
+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 <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];
+ size_t 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h
new file mode 100644
index 0000000000..c3f91a87b0
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c
new file mode 100644
index 0000000000..50b9f74a5b
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c
@@ -0,0 +1,888 @@
+/*
+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 <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);
+ assert(ctx->my_addr.protocol==ctx->peer_addr.protocol);
+
+ 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,
+ const nr_transport_addr* peer_addr) {
+ int r,_status;
+ int mode;
+ NR_async_cb finished_cb;
+ void *cb_arg;
+ if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING)
+ ABORT(R_NOT_PERMITTED);
+
+ mode = ctx->mode;
+ finished_cb = ctx->finished_cb;
+ cb_arg = ctx->cb_arg;
+
+ nr_stun_client_reset(ctx);
+ nr_transport_addr_copy(&ctx->peer_addr, peer_addr);
+
+ 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);
+
+ // track retransmits for ice telemetry
+ nr_accumulate_count(&(ctx->retransmit_ct), 1);
+
+ /* 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);
+ }
+ if (_status) {
+ // cb doesn't return anything, but we should probably log that we aborted
+ // This also quiets the unused variable warnings.
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired cb abort with status: %d",
+ ctx->label, _status);
+ }
+ 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;
+ int allow_unauthed_redirect = 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:
+ /* If the STUN server responds with an error, give up, since we don't
+ * want to delay the completion of gathering. */
+ fail_on_error = 1;
+ 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:
+ /* If the STUN server responds with an error, give up, since we don't
+ * want to delay the completion of gathering. */
+ fail_on_error = 1;
+ break;
+
+ case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96:
+ /* If the STUN server responds with an error, give up, since we don't
+ * want to delay the completion of gathering. */
+ fail_on_error = 1;
+ break;
+
+#ifdef USE_ICE
+ case NR_ICE_CLIENT_MODE_BINDING_REQUEST:
+ /* We do not set fail_on_error here. The error might be transient, and
+ * retrying isn't going to cause a slowdown. */
+ password = &ctx->params.ice_binding_request.password;
+ break;
+ case NR_ICE_CLIENT_MODE_USE_CANDIDATE:
+ /* We do not set fail_on_error here. The error might be transient, and
+ * retrying isn't going to cause a slowdown. */
+ 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;
+ /* Do not require mutual auth on redirect responses to Allocate requests. */
+ allow_unauthed_redirect = 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;
+
+ if (allow_unauthed_redirect &&
+ nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_ERROR_CODE,
+ &attr) &&
+ (attr->u.error_code.number / 100 == 3)) {
+ password = 0;
+ }
+
+/* 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))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): long term auth failed",ctx->label);
+ ABORT(r);
+ }
+ }
+ else {
+ if ((r=nr_stun_receive_response_short_term_auth(ctx->response))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): short term auth failed",ctx->label);
+ ABORT(r);
+ }
+ }
+ }
+
+ if (NR_STUN_GET_TYPE_CLASS(ctx->response->header.type) == NR_CLASS_RESPONSE) {
+ if ((r=nr_stun_process_success_response(ctx->response))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_process_success_response failed",ctx->label);
+ 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))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_process_error_response failed",ctx->label);
+ ABORT(r);
+ }
+ else {
+ ctx->error_code = 0xffff;
+ /* drop the error on the floor */
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): processed error response",ctx->label);
+ 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)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label);
+ ABORT(R_BAD_DATA);
+ }
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label);
+ 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 {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS or MAPPED-ADDRESS",ctx->label);
+ 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)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MAPPED-ADDRESS",ctx->label);
+ 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)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label);
+ ABORT(R_BAD_DATA);
+ }
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label);
+ 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)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label);
+ ABORT(R_BAD_DATA);
+ }
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label);
+ 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)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label);
+ ABORT(R_BAD_DATA);
+ }
+ if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label);
+ ABORT(R_BAD_DATA);
+ }
+
+ if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_RELAY_ADDRESS, &attr)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-RELAYED-ADDRESS",ctx->label);
+ ABORT(R_BAD_DATA);
+ }
+
+ if ((r=nr_stun_transport_addr_check(&attr->u.relay_address.unmasked,
+ ctx->mapped_addr_check_mask))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_transport_addr_check failed",ctx->label);
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_copy(
+ &ctx->results.allocate_response.relay_addr,
+ &attr->u.relay_address.unmasked))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label);
+ ABORT(r);
+ }
+
+ if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No LIFETIME",ctx->label);
+ 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)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label);
+ ABORT(R_BAD_DATA);
+ }
+ if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No LIFETIME",ctx->label);
+ 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)) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label);
+ 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))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): XOR-MAPPED-ADDRESS is bogus",ctx->label);
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.xor_mapped_address.unmasked))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label);
+ 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))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): MAPPED-ADDRESS is bogus",ctx->label);
+ ABORT(r);
+ }
+
+ if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.mapped_address))) {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label);
+ ABORT(r);
+ }
+ }
+ else {
+ r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No mapped address!",ctx->label);
+ 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h
new file mode 100644
index 0000000000..0cc9045e2a
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h
@@ -0,0 +1,200 @@
+/*
+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;
+ UINT2 request_ct;
+ UINT2 retransmit_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;
+ 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,
+ const nr_transport_addr* peer_addr);
+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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c
new file mode 100644
index 0000000000..ae748a667b
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c
@@ -0,0 +1,1550 @@
+/*
+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 <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"
+
+#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, size_t buflen, UCHAR *buf, size_t *offset);
+static int nr_stun_encode_htonl(UINT4 data, size_t buflen, UCHAR *buf, size_t *offset);
+static int nr_stun_encode_htonll(UINT8 data, size_t buflen, UCHAR *buf, size_t *offset);
+static int nr_stun_encode(UCHAR *data, size_t length, size_t buflen, UCHAR *buf, size_t *offset);
+
+static int nr_stun_decode_htons(UCHAR *buf, size_t buflen, size_t *offset, UINT2 *data);
+static int nr_stun_decode_htonl(UCHAR *buf, size_t buflen, size_t *offset, UINT4 *data);
+static int nr_stun_decode_htonll(UCHAR *buf, size_t buflen, size_t *offset, UINT8 *data);
+static int nr_stun_decode(size_t length, UCHAR *buf, size_t buflen, size_t *offset, UCHAR *data);
+
+static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t max_chars);
+
+static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data);
+static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data);
+static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data);
+static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data);
+static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data);
+static int
+nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data);
+
+
+int
+nr_stun_encode_htons(UINT2 data, size_t buflen, UCHAR *buf, size_t *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, size_t buflen, UCHAR *buf, size_t *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, size_t buflen, UCHAR *buf, size_t *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, size_t length, size_t buflen, UCHAR *buf, size_t *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, size_t buflen, size_t *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, size_t buflen, size_t *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, size_t buflen, size_t *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(size_t length, UCHAR *buf, size_t buflen, size_t *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;
+}
+
+/**
+ * The argument must be a non-null pointer to a zero-terminated string.
+ *
+ * If the argument is valid UTF-8, returns the number of code points in the
+ * string excluding the zero-terminator.
+ *
+ * If the argument is invalid UTF-8, returns a lower bound for the number of
+ * code points in the string. (If UTF-8 error handling was performed on the
+ * string, new REPLACEMENT CHARACTER code points could be introduced in
+ * a way that would increase the total number of code points compared to
+ * what this function counts.)
+ */
+size_t
+nr_count_utf8_code_points_without_validation(const char *s) {
+ size_t nchars = 0;
+ char c;
+ while ((c = *s)) {
+ if ((c & 0xC0) != 0x80) {
+ ++nchars;
+ }
+ ++s;
+ }
+ return nchars;
+}
+
+int
+nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t 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);
+ }
+
+ nchars = nr_count_utf8_code_points_without_validation(s);
+ 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, size_t 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, size_t 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, size_t 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, size_t 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, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data)
+{
+ int _status;
+ nr_stun_attr_error_code *result = data;
+ UCHAR pad[2];
+ UCHAR class;
+ UCHAR number;
+ size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data)
+{
+ int r,_status;
+ nr_stun_attr_fingerprint *fingerprint = data;
+ nr_stun_message_header *header = (nr_stun_message_header*)buf;
+ size_t 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 */
+ if (length + sizeof(*header) != buflen) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Fingerprint is not final attribute in message");
+ ABORT(R_FAILED);
+ }
+
+ 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t offset, size_t buflen, UCHAR *buf, size_t *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, size_t attrlen, UCHAR *buf, size_t offset, size_t 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)
+ NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_BANDWIDTH, "BANDWIDTH", nr_stun_attr_codec_UINT4, 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;
+ size_t 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;
+}
+
+// Since this sanity check is only a collection of assert statements and those
+// assert statements are compiled out in non-debug builds, undef SANITY_CHECKS
+// so we can avoid the warning that padding_bytes is never used in opt builds.
+#ifdef NDEBUG
+#undef SANITY_CHECKS
+#endif
+
+#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;
+ size_t length_offset;
+ size_t 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_t)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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h
new file mode 100644
index 0000000000..4e4ff60e0c
--- /dev/null
+++ b/dom/media/webrtc/transport/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, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen);
+ int (*decode)(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t 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, size_t 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;
+
+size_t nr_count_utf8_code_points_without_validation(const char *s);
+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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c
new file mode 100644
index 0000000000..8f118e8942
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c
@@ -0,0 +1,245 @@
+/*
+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 <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, size_t 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, size_t 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, size_t 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, size_t 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, size_t 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h
new file mode 100644
index 0000000000..c2badc1d2b
--- /dev/null
+++ b/dom/media/webrtc/transport/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, size_t len);
+int nr_is_stun_request_message(UCHAR *buf, size_t len);
+int nr_is_stun_response_message(UCHAR *buf, size_t len);
+int nr_is_stun_indication_message(UCHAR *buf, size_t len);
+int nr_has_stun_cookie(UCHAR *buf, size_t len);
+int nr_stun_message_length(UCHAR *buf, int len, int *length);
+
+#endif
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c
new file mode 100644
index 0000000000..7e01686109
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c
@@ -0,0 +1,364 @@
+/*
+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 <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, size_t 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;
+ nr_stun_message_get_attribute(msg, type, 0, &attr);
+
+ if (attribute)
+ *attribute = attr;
+
+ return attr ? 1 : 0;
+}
+
+int
+nr_stun_message_get_attribute(nr_stun_message *msg, UINT2 type, UINT2 index, nr_stun_message_attribute **attribute)
+{
+ nr_stun_message_attribute *attr;
+ TAILQ_FOREACH(attr, &msg->attributes, entry) {
+ if (attr->type == type && !attr->invalid) {
+ if (!index) {
+ *attribute = attr;
+ return 0;
+ }
+ --index;
+ }
+ }
+ *attribute = 0;
+ return R_NOT_FOUND;
+}
+
+#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;
+ (void)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,
+ { (void)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,
+ { (void)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,
+ { (void)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,
+ { (void)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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h
new file mode 100644
index 0000000000..ffd68d3eee
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h
@@ -0,0 +1,208 @@
+/*
+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];
+ size_t 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;
+ size_t 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];
+ size_t 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, size_t 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_get_attribute(nr_stun_message *msg, UINT2 type, UINT2 index, 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c
new file mode 100644
index 0000000000..13366e265d
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c
@@ -0,0 +1,554 @@
+/*
+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 <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");
+ r_log(NR_LOG_STUN, LOG_WARNING, "Request contains comprehension required but 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) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Indication contains comprehension required but unknown attributes");
+ 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) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Response contains comprehension required but unknown attributes");
+ 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) {
+ r_log(NR_LOG_STUN, LOG_WARNING, "Error response contains comprehension required but unknown attributes");
+ 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:
+ /* We do not treat STUN/300 as retryable. The TURN Allocate handling
+ * code will reset the ctx if appropriate. */
+ 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h
new file mode 100644
index 0000000000..5975670779
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h
new file mode 100644
index 0000000000..2167fcd91b
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c
new file mode 100644
index 0000000000..b92b6b5ab6
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c
@@ -0,0 +1,468 @@
+/*
+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.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_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);
+
+ 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;
+ nr_transport_addr my_addr;
+
+ if ((r=nr_socket_getaddr(sock, &my_addr))) {
+ ABORT(r);
+ }
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,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,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;
+ char string[256];
+ nr_transport_addr my_addr;
+
+ if ((r=nr_socket_getaddr(sock, &my_addr))) {
+ ABORT(r);
+ }
+
+ r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(label=%s): Sending(my_addr=%s,peer_addr=%s)",ctx->label,my_addr.as_string,peer_addr->as_string);
+
+ 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,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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h
new file mode 100644
index 0000000000..328675ee22
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h
@@ -0,0 +1,80 @@
+/*
+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_stun_server_client_head clients;
+ nr_stun_server_client *default_client;
+};
+
+
+int nr_stun_server_ctx_create(char *label, 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c
new file mode 100644
index 0000000000..cbb0128924
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c
@@ -0,0 +1,352 @@
+/*
+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 <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 (size_t 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_filter_local_addresses(nr_local_addr addrs[], int *count)
+{
+ int r,_status;
+ char allow_loopback = 0;
+ char allow_link_local = 0;
+
+ if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS,
+ &allow_loopback))) {
+ if (r != R_NOT_FOUND) {
+ ABORT(r);
+ }
+ }
+
+ if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS,
+ &allow_link_local))) {
+ if (r != R_NOT_FOUND) {
+ ABORT(r);
+ }
+ }
+
+ if ((r=nr_stun_filter_addrs(addrs,
+ !allow_loopback,
+ !allow_link_local,
+ count))) {
+ ABORT(r);
+ }
+
+ _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 0
+ // this really goes with the code commented out below. (mjf)
+ if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count)))
+ if (r != R_NOT_FOUND)
+ ABORT(r);
+#endif
+
+ if (*count == 0) {
+ if ((r=nr_stun_get_addrs(addrs, maxaddrs, 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, size_t len, nr_stun_message *req)
+{
+ int _status;
+ nr_stun_message_header header;
+ char reqid[44];
+ char msgid[44];
+ size_t unused;
+
+ 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), &unused);
+ nr_nbin2hex((UCHAR*)&header.id, sizeof(header.id), msgid, sizeof(msgid), &unused);
+ 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;
+}
+
+#ifndef UINT2_MAX
+#define UINT2_MAX ((UINT2)(65535U))
+#endif
+
+void nr_accumulate_count(UINT2* orig_count, UINT2 add_count)
+ {
+ if (UINT2_MAX - add_count < *orig_count) {
+ // don't rollover, just stop accumulating at MAX value
+ *orig_count = UINT2_MAX;
+ } else {
+ *orig_count += add_count;
+ }
+ }
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h
new file mode 100644
index 0000000000..379b4fdd3d
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h
@@ -0,0 +1,62 @@
+/*
+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);
+
+// removes duplicates, and based on prefs, loopback and link_local addresses
+int nr_stun_filter_local_addresses(nr_local_addr addrs[], int *count);
+
+int nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count);
+
+int nr_stun_different_transaction(UCHAR *msg, size_t len, nr_stun_message *req);
+
+char* nr_stun_msg_type(int type);
+
+int nr_random_alphanum(char *alphanum, int size);
+
+// accumulate a count without worrying about rollover
+void nr_accumulate_count(UINT2* orig_count, UINT2 add_count);
+
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c
new file mode 100644
index 0000000000..707d43a4a9
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c
@@ -0,0 +1,1277 @@
+/*
+ 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.
+*/
+
+#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"
+#include "ice_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,
+ const nr_transport_addr *addr,
+ nr_turn_permission **permp);
+static int nr_turn_permission_find(nr_turn_client_ctx *ctx,
+ const 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);
+
+int nr_transport_addr_listnode_create(const nr_transport_addr *addr, nr_transport_addr_listnode **listnodep)
+{
+ nr_transport_addr_listnode *listnode = 0;
+ int r,_status;
+
+ if (!(listnode=RCALLOC(sizeof(nr_transport_addr_listnode)))) {
+ ABORT(R_NO_MEMORY);
+ }
+
+ if ((r = nr_transport_addr_copy(&listnode->value, addr))) {
+ ABORT(r);
+ }
+
+ *listnodep = listnode;
+ listnode = 0;
+ _status = 0;
+
+abort:
+ nr_transport_addr_listnode_destroy(&listnode);
+ return(_status);
+}
+
+void nr_transport_addr_listnode_destroy(nr_transport_addr_listnode **listnode)
+{
+ RFREE(*listnode);
+ *listnode = 0;
+}
+
+/* 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;
+ STAILQ_INIT(&sctx->addresses_tried);
+
+ /* 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);
+
+ while (!STAILQ_EMPTY(&ctx->addresses_tried)) {
+ nr_transport_addr_listnode *listnode = STAILQ_FIRST(&ctx->addresses_tried);
+ STAILQ_REMOVE_HEAD(&ctx->addresses_tried, entry);
+ nr_transport_addr_listnode_destroy(&listnode);
+ }
+
+ 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;
+ nr_transport_addr_listnode *address_tried = 0;
+
+ 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);
+ }
+
+ if ((r=nr_transport_addr_listnode_create(&ctx->stun->peer_addr, &address_tried))) {
+ ABORT(r);
+ }
+
+ STAILQ_INSERT_TAIL(&ctx->addresses_tried, address_tried, entry);
+
+ _status=0;
+abort:
+ return _status;
+}
+
+static int nr_turn_stun_ctx_handle_redirect(nr_turn_stun_ctx *ctx)
+{
+ int r, _status;
+ nr_turn_client_ctx *tctx = ctx->tctx;
+ nr_stun_message_attribute *ec;
+ nr_stun_message_attribute *attr;
+ nr_transport_addr *alternate_addr = 0;
+ int index = 0;
+
+ if (!tctx->ctx) {
+ /* If we were to require TCP nr_sockets to allow multiple connect calls by
+ * disconnecting and re-connecting, we could avoid this requirement. */
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect is not supported when "
+ "there is no ICE ctx, and by extension no socket factory, since we "
+ "need that to create new sockets in the TCP case",
+ tctx->label);
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (ctx->stun->response->header.type != NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE) {
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect called for something "
+ "other than an Allocate error response (type=%d)",
+ tctx->label, ctx->stun->response->header.type);
+ ABORT(R_BAD_ARGS);
+ }
+
+ if (!nr_stun_message_has_attribute(ctx->stun->response,
+ NR_STUN_ATTR_ERROR_CODE, &ec) ||
+ (ec->u.error_code.number != 300)) {
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect called without a "
+ "300 response",
+ tctx->label);
+ ABORT(R_BAD_ARGS);
+ }
+
+ while (!alternate_addr && !nr_stun_message_get_attribute(
+ ctx->stun->response, NR_STUN_ATTR_ALTERNATE_SERVER, index++, &attr)) {
+ alternate_addr = &attr->u.alternate_server;
+
+ // TODO: Someday we may need to handle IP version switching, but it is
+ // unclear how that is supposed to work with ICE when the IP version of
+ // the candidate's base address is fixed...
+ if (alternate_addr->ip_version != tctx->turn_server_addr.ip_version) {
+ r_log(NR_LOG_TURN, LOG_INFO,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect not trying %s, since it is a different IP version",
+ tctx->label, alternate_addr->as_string);
+ alternate_addr = 0;
+ continue;
+ }
+
+ /* Check if we've already tried this, and ignore if we have */
+ nr_transport_addr_listnode *address_tried = 0;
+ STAILQ_FOREACH(address_tried, &ctx->addresses_tried, entry) {
+ /* Ignore protocol */
+ alternate_addr->protocol = address_tried->value.protocol;
+ if (!nr_transport_addr_cmp(alternate_addr, &address_tried->value, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) {
+ r_log(NR_LOG_TURN, LOG_INFO,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect already tried %s, ignoring",
+ tctx->label, alternate_addr->as_string);
+ alternate_addr = 0;
+ break;
+ }
+ }
+ }
+
+ if (!alternate_addr) {
+ /* Should we use a different error code depending on why we didn't find
+ * one? (eg; no ALTERNATE-SERVERS at all, none that we have not tried
+ * already, none that are of a compatible IP version?) */
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect did not find a viable "
+ "ALTERNATE-SERVER",
+ tctx->label);
+ ABORT(R_FAILED);
+ }
+
+ r_log(NR_LOG_TURN, LOG_INFO,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect trying %s",
+ tctx->label, alternate_addr->as_string);
+
+ /* This also handles the call to nr_transport_addr_fmt_addr_string */
+ if ((r = nr_transport_addr_copy_addrport(&tctx->turn_server_addr,
+ alternate_addr))) {
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect copying ALTERNATE-SERVER "
+ "failed(%d)!",
+ tctx->label, r);
+ assert(0);
+ ABORT(r);
+ }
+
+ /* TURN server address is now updated. Restart the STUN Allocate ctx. Note
+ * that we do not attempt to update the local address; if the TURN server
+ * redirects to something that is not best reached from the already-selected
+ * local address, oh well. */
+
+ if (tctx->turn_server_addr.protocol == IPPROTO_TCP) {
+ /* For TCP, we need to replace the underlying nr_socket, since we cannot
+ * un-connect it from the old server. */
+ /* If we were to require TCP nr_sockets to allow multiple connect calls by
+ * disconnecting and re-connecting, we could avoid this stuff, and just
+ * call nr_socket_connect. */
+ nr_transport_addr old_local_addr;
+ nr_socket* new_socket;
+ if ((r = nr_socket_getaddr(tctx->sock, &old_local_addr))) {
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect "
+ "failed to get old local address (%d)!",
+ tctx->label, r);
+ assert(0);
+ ABORT(r);
+ }
+
+ if ((r = nr_socket_factory_create_socket(tctx->ctx->socket_factory,
+ &old_local_addr, &new_socket))) {
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect "
+ "failed to create new raw TCP socket for redirect (%d)!",
+ tctx->label, r);
+ assert(0);
+ ABORT(r);
+ }
+
+ if ((r = nr_socket_buffered_stun_reset(tctx->sock, new_socket))) {
+ /* nr_socket_buffered_stun_reset always takes ownership of |new_socket| */
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect "
+ "failed to update raw TCP socket (%d)!",
+ tctx->label, r);
+ assert(0);
+ ABORT(r);
+ }
+
+ if ((r = nr_socket_connect(tctx->sock, &tctx->turn_server_addr))) {
+ if (r != R_WOULDBLOCK) {
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect nr_socket_connect "
+ "failed(%d)!",
+ tctx->label, r);
+ assert(0);
+ ABORT(r);
+ }
+ }
+ }
+
+ nr_transport_addr_copy(&ctx->stun->peer_addr, &tctx->turn_server_addr);
+
+ if ((r = nr_turn_stun_ctx_start(ctx))) {
+ r_log(NR_LOG_TURN, LOG_ERR,
+ "TURN(%s): nr_turn_stun_ctx_handle_redirect nr_turn_stun_ctx_start "
+ "failed(%d)!",
+ tctx->label, r);
+ assert(0);
+ 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 == 438) {
+ // track 438s for ice telemetry
+ nr_accumulate_count(&(ctx->tctx->cnt_438s), 1);
+ }
+ if (ctx->stun->error_code == 401 || ctx->stun->error_code == 438) {
+ if (ctx->retry_ct > 0) {
+ if (ctx->stun->error_code == 401) {
+ // track 401s for ice telemetry
+ nr_accumulate_count(&(ctx->tctx->cnt_401s), 1);
+ }
+ 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 if (ctx->stun->error_code == 300) {
+ r_log(NR_LOG_TURN, LOG_INFO,
+ "TURN(%s): Redirect received, restarting TURN", ctx->tctx->label);
+ /* We don't limit this with a retry counter, we limit redirects by
+ * checking whether we've tried the ALTERNATE-SERVER yet. */
+ ctx->retry_ct = 0;
+ if ((r = nr_turn_stun_ctx_handle_redirect(ctx))) {
+ ABORT(r);
+ }
+ } 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_ice_ctx* ice_ctx,
+ 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);
+ }
+ }
+
+ ctx->ctx = ice_ctx;
+
+ *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) {
+ // track 403s for ice telemetry
+ nr_accumulate_count(&(ctx->tctx->cnt_403s), 1);
+ 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, const 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, const 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, const 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, const 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h
new file mode 100644
index 0000000000..2049f1bdfb
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h
@@ -0,0 +1,161 @@
+/*
+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
+
+struct nr_ice_ctx_;
+
+typedef struct nr_transport_addr_listnode_ {
+ nr_transport_addr value;
+ STAILQ_ENTRY(nr_transport_addr_listnode_) entry;
+} nr_transport_addr_listnode;
+typedef STAILQ_HEAD(nr_transport_addr_listnode_head_, nr_transport_addr_listnode_) nr_transport_addr_listnode_head;
+
+/*
+ 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;
+
+ nr_transport_addr_listnode_head addresses_tried;
+
+ 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;
+
+ /* We need access to the socket factory to create new TCP sockets for handling
+ * STUN/300 responses. */
+ /* If we were to require TCP nr_sockets to allow multiple connect calls by
+ * disconnecting and re-connecting, we could avoid this requirement. */
+ struct nr_ice_ctx_* ctx;
+
+ NR_async_cb finished_cb;
+ void *cb_arg;
+
+ void *connected_timer_handle;
+ void *refresh_timer_handle;
+
+ // ice telemetry
+ UINT2 cnt_401s;
+ UINT2 cnt_403s;
+ UINT2 cnt_438s;
+} nr_turn_client_ctx;
+
+extern int NR_LOG_TURN;
+
+int nr_transport_addr_listnode_create(const nr_transport_addr *addr, nr_transport_addr_listnode **listnodep);
+void nr_transport_addr_listnode_destroy(nr_transport_addr_listnode **listnode);
+int nr_turn_client_ctx_create(const char* label, nr_socket* sock,
+ const char* username, Data* password,
+ nr_transport_addr* addr,
+ struct nr_ice_ctx_* ice_ctx,
+ 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, const 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,
+ const nr_transport_addr *addr);
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.c b/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.c
new file mode 100644
index 0000000000..81f7731d74
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.c
@@ -0,0 +1,57 @@
+/*
+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>
+#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/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.h b/dom/media/webrtc/transport/third_party/nICEr/src/util/cb_args.h
new file mode 100644
index 0000000000..83f7831ccc
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.c b/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.c
new file mode 100644
index 0000000000..249cfe350f
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.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.
+*/
+
+#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/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.h b/dom/media/webrtc/transport/third_party/nICEr/src/util/ice_util.h
new file mode 100644
index 0000000000..44751edaf8
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nICEr/upstream.diff b/dom/media/webrtc/transport/third_party/nICEr/upstream.diff
new file mode 100644
index 0000000000..a664fd9d21
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/COPYRIGHT b/dom/media/webrtc/transport/third_party/nrappkit/COPYRIGHT
new file mode 100644
index 0000000000..b0bb25595e
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/IMPORT_FILES b/dom/media/webrtc/transport/third_party/nrappkit/IMPORT_FILES
new file mode 100644
index 0000000000..0f80a52e33
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/README b/dom/media/webrtc/transport/third_party/nrappkit/README
new file mode 100644
index 0000000000..9cf43319b3
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/README_MOZILLA b/dom/media/webrtc/transport/third_party/nrappkit/README_MOZILLA
new file mode 100644
index 0000000000..92102c83c6
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/VERSION b/dom/media/webrtc/transport/third_party/nrappkit/VERSION
new file mode 100644
index 0000000000..3ef69b306c
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/VERSION
@@ -0,0 +1 @@
+1.0b2
diff --git a/dom/media/webrtc/transport/third_party/nrappkit/nrappkit.gyp b/dom/media/webrtc/transport/third_party/nrappkit/nrappkit.gyp
new file mode 100644
index 0000000000..c3f88af4bc
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/nrappkit.gyp
@@ -0,0 +1,251 @@
+# 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',
+
+ # 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',
+ '-Wno-format',
+ '-Wno-format-security',
+ ],
+ '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',
+ ],
+ }],
+
+ # Windows, clang-cl build
+ [ 'clang_cl == 1', {
+ 'cflags_mozilla': [
+ '-Xclang',
+ '-Wall',
+ '-Xclang',
+ '-Wno-parentheses',
+ '-Wno-strict-prototypes',
+ '-Wmissing-prototypes',
+ '-Wno-format',
+ '-Wno-format-security',
+ ],
+ }],
+
+ ## Linux/Android
+ [ '(OS == "linux") or (OS == "android")', {
+ 'cflags_mozilla': [
+ '-Wall',
+ '-Wno-parentheses',
+ '-Wno-strict-prototypes',
+ '-Wmissing-prototypes',
+ '-Wno-format',
+ '-Wno-format-security',
+ ],
+ '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/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_timer.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_timer.h
new file mode 100644
index 0000000000..544a3d44fd
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h
new file mode 100644
index 0000000000..58bcd435eb
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait_int.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait_int.h
new file mode 100644
index 0000000000..d17372d183
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c
new file mode 100644
index 0000000000..09bb24749f
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c
@@ -0,0 +1,696 @@
+/**
+ 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
+ */
+
+#ifdef LINUX
+#define _BSD_SOURCE
+#define _DEFAULT_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((size_t)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((size_t)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:
+ (void)_status; // to avoid unused variable warning and still conform to
+ // pattern of using 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;
+ size_t 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((size_t)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((size_t)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(r_log_initted==0) {
+#ifdef WIN32
+ r_log_env_verbose=1;
+#else
+ log=getenv("R_LOG_VERBOSE");
+ if(log)
+ r_log_env_verbose=atoi(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;
+ }
+ }
+
+ 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/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.h b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.h
new file mode 100644
index 0000000000..a72dfa0667
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/plugin/nr_plugin.h b/dom/media/webrtc/transport/third_party/nrappkit/src/plugin/nr_plugin.h
new file mode 100644
index 0000000000..56da0624a6
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/android_funcs.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/android_funcs.h
new file mode 100644
index 0000000000..d48bbe1373
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/csi_platform.h
new file mode 100644
index 0000000000..bcfb2bce77
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/sys/ttycom.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/include/sys/ttycom.h
new file mode 100644
index 0000000000..852bf9103b
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/port-impl.mk b/dom/media/webrtc/transport/third_party/nrappkit/src/port/android/port-impl.mk
new file mode 100644
index 0000000000..6704cfedf9
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
new file mode 100644
index 0000000000..41f1a5b097
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include/sys/queue.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/generic/include/sys/queue.h
new file mode 100644
index 0000000000..4d7b998a34
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h
new file mode 100644
index 0000000000..8aefaf9249
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/linux_funcs.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/linux_funcs.h
new file mode 100644
index 0000000000..1619136306
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/sys/ttycom.h
new file mode 100644
index 0000000000..6f8a3066d6
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/port-impl.mk b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/port-impl.mk
new file mode 100644
index 0000000000..6704cfedf9
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h
new file mode 100644
index 0000000000..27c64c53d9
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.c
new file mode 100644
index 0000000000..3e71ca37d9
--- /dev/null
+++ b/dom/media/webrtc/transport/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;
+ unsigned 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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/c2ru.h
new file mode 100644
index 0000000000..0f8c38ecc2
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c
new file mode 100644
index 0000000000..709b1c3fb7
--- /dev/null
+++ b/dom/media/webrtc/transport/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(NR_registry 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(NR_registry 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;
+ size_t 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;
+ size_t plen;
+ size_t clen;
+ char *c;
+ size_t 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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.h
new file mode 100644
index 0000000000..b48893ba72
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_int.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_int.h
new file mode 100644
index 0000000000..d6d412c543
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_local.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_local.c
new file mode 100644
index 0000000000..ed6e19aaa0
--- /dev/null
+++ b/dom/media/webrtc/transport/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(NR_registry 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 NRREGLOCALGET(func, TYPE, type) \
+int \
+func(NR_registry name, type *out) \
+{ \
+ return nr_reg_get(name, TYPE, out); \
+}
+
+NRREGLOCALGET(nr_reg_local_get_char, NR_REG_TYPE_CHAR, char)
+NRREGLOCALGET(nr_reg_local_get_uchar, NR_REG_TYPE_UCHAR, UCHAR)
+NRREGLOCALGET(nr_reg_local_get_int2, NR_REG_TYPE_INT2, INT2)
+NRREGLOCALGET(nr_reg_local_get_uint2, NR_REG_TYPE_UINT2, UINT2)
+NRREGLOCALGET(nr_reg_local_get_int4, NR_REG_TYPE_INT4, INT4)
+NRREGLOCALGET(nr_reg_local_get_uint4, NR_REG_TYPE_UINT4, UINT4)
+NRREGLOCALGET(nr_reg_local_get_int8, NR_REG_TYPE_INT8, INT8)
+NRREGLOCALGET(nr_reg_local_get_uint8, NR_REG_TYPE_UINT8, UINT8)
+NRREGLOCALGET(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 NRREGLOCALSET(func, TYPE, type) \
+int \
+func(NR_registry name, type data) \
+{ \
+ return nr_reg_set(name, TYPE, &data); \
+}
+
+NRREGLOCALSET(nr_reg_local_set_char, NR_REG_TYPE_CHAR, char)
+NRREGLOCALSET(nr_reg_local_set_uchar, NR_REG_TYPE_UCHAR, UCHAR)
+NRREGLOCALSET(nr_reg_local_set_int2, NR_REG_TYPE_INT2, INT2)
+NRREGLOCALSET(nr_reg_local_set_uint2, NR_REG_TYPE_UINT2, UINT2)
+NRREGLOCALSET(nr_reg_local_set_int4, NR_REG_TYPE_INT4, INT4)
+NRREGLOCALSET(nr_reg_local_set_uint4, NR_REG_TYPE_UINT4, UINT4)
+NRREGLOCALSET(nr_reg_local_set_int8, NR_REG_TYPE_INT8, INT8)
+NRREGLOCALSET(nr_reg_local_set_uint8, NR_REG_TYPE_UINT8, UINT8)
+NRREGLOCALSET(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(NR_registry 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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_vtbl.h b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry_vtbl.h
new file mode 100644
index 0000000000..0f75a03b95
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registrycb.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registrycb.c
new file mode 100644
index 0000000000..4b326a1ee2
--- /dev/null
+++ b/dom/media/webrtc/transport/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;
+ size_t 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(NR_registry 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;
+ size_t 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(NR_registry name, char action, void (*cb)(void *cb_arg, char action, NR_registry name))
+{
+ int r, _status;
+ size_t 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/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_api.h b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_api.h
new file mode 100644
index 0000000000..ce6055f0d9
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_common.h b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_common.h
new file mode 100644
index 0000000000..ac3ff44004
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_reg_keys.h b/dom/media/webrtc/transport/third_party/nrappkit/src/share/nr_reg_keys.h
new file mode 100644
index 0000000000..b051f51d4a
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/stats/nrstats.h b/dom/media/webrtc/transport/third_party/nrappkit/src/stats/nrstats.h
new file mode 100644
index 0000000000..0b6c2bc3c3
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.c
new file mode 100644
index 0000000000..64689accfa
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.c
@@ -0,0 +1,73 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/byteorder.h
new file mode 100644
index 0000000000..8df0589a63
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.c
new file mode 100644
index 0000000000..8212988eda
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.c
@@ -0,0 +1,109 @@
+/**
+ 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
+ */
+
+#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, size_t binlen, char hex[], size_t size, size_t *len)
+{
+ int _status;
+ size_t i;
+ size_t hexlen;
+
+ hexlen = 2*binlen;
+ if (size < hexlen)
+ ABORT(R_BAD_ARGS);
+
+ for (i = 0; i < binlen; ++i) {
+ *hex++ = bin2hex_map[bin[i]][0];
+ *hex++ = bin2hex_map[bin[i]][1];
+ }
+
+ if (size >= hexlen+1)
+ *hex = '\0';
+
+ *len = hexlen;
+
+ _status=0;
+ abort:
+ return(_status);
+}
+
+
+int
+nr_nhex2bin(char *hex, size_t hexlen, UCHAR bin[], size_t size, size_t *len)
+{
+ int _status;
+ size_t binlen;
+ int h1;
+ int h2;
+ size_t 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/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/hex.h
new file mode 100644
index 0000000000..8c493b533b
--- /dev/null
+++ b/dom/media/webrtc/transport/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, size_t binlen, char hex[], size_t size, size_t *len);
+int nr_nhex2bin(char *hex, size_t hexlen, UCHAR bin[], size_t size, size_t *len);
+
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/assoc.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/assoc.h
new file mode 100644
index 0000000000..013e788685
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.c
new file mode 100644
index 0000000000..fa796217ea
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.c
@@ -0,0 +1,127 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/debug.h
new file mode 100644
index 0000000000..34f7b2fb54
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.c
new file mode 100644
index 0000000000..25b3827d50
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.c
@@ -0,0 +1,539 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_assoc.h
new file mode 100644
index 0000000000..5311542c9d
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_common.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_common.h
new file mode 100644
index 0000000000..c11bb3d0fd
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c
new file mode 100644
index 0000000000..38d3e4da38
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c
@@ -0,0 +1,175 @@
+/**
+ 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>
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.h
new file mode 100644
index 0000000000..c11be4e04d
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.c
new file mode 100644
index 0000000000..dfb7af2d5c
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.c
@@ -0,0 +1,248 @@
+/**
+ 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
+ */
+
+#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;
+ size_t 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);
+
+ if (d) {
+ 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;
+ size_t 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;
+ size_t 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;
+ size_t 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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_data.h
new file mode 100644
index 0000000000..3edf7b287a
--- /dev/null
+++ b/dom/media/webrtc/transport/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;
+ size_t len;
+} Data;
+
+int r_data_create(Data **dp,const UCHAR *d,size_t l);
+int r_data_alloc(Data **dp, size_t l);
+int r_data_make(Data *dp, const UCHAR *d,size_t l);
+int r_data_alloc_mem(Data *d,size_t 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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_defaults.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_defaults.h
new file mode 100644
index 0000000000..0ec91a0331
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.c
new file mode 100644
index 0000000000..e770e02438
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.c
@@ -0,0 +1,136 @@
+/**
+ 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
+ */
+
+#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];
+ size_t 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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_errors.h
new file mode 100644
index 0000000000..f52375b98d
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_includes.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_includes.h
new file mode 100644
index 0000000000..4fb7af5643
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.c
new file mode 100644
index 0000000000..4e71d67030
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.c
@@ -0,0 +1,273 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_list.h
new file mode 100644
index 0000000000..bbefc6c39f
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h
new file mode 100644
index 0000000000..ddfedd3c36
--- /dev/null
+++ b/dom/media/webrtc/transport/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 NR_UNIMPLEMENTED do { fprintf(stderr,"%s:%d Function %s unimplemented\n",__FILE__,__LINE__,__FUNCTION__); abort(); } while(0)
+
+#include "r_memory.h"
+
+#endif
diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.c
new file mode 100644
index 0000000000..53846fc019
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.c
@@ -0,0 +1,198 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_memory.h
new file mode 100644
index 0000000000..4357070767
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_replace.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_replace.c
new file mode 100644
index 0000000000..8916b884cc
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_replace.c
@@ -0,0 +1,107 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_thread.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_thread.h
new file mode 100644
index 0000000000..212900bcc4
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.c
new file mode 100644
index 0000000000..f873585c4b
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.c
@@ -0,0 +1,235 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h
new file mode 100644
index 0000000000..1ee1e778fe
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_types.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_types.h
new file mode 100644
index 0000000000..d00810a7a2
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.c
new file mode 100644
index 0000000000..459baecdda
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.c
@@ -0,0 +1,215 @@
+/**
+ 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
+ */
+
+#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/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/p_buf.h
new file mode 100644
index 0000000000..29881960c6
--- /dev/null
+++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c
new file mode 100644
index 0000000000..17d49639fd
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c
@@ -0,0 +1,775 @@
+/**
+ 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
+ */
+
+#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) && WINVER < 0x0600)
+#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;
+ }
+#endif
+
diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h
new file mode 100644
index 0000000000..975baa4aa2
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h
@@ -0,0 +1,73 @@
+/**
+ 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);
+
+#if defined(WIN32) && WINVER < 0x0600
+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/dom/media/webrtc/transport/third_party/nrappkit/upstream.diff b/dom/media/webrtc/transport/third_party/nrappkit/upstream.diff
new file mode 100644
index 0000000000..50b88961dc
--- /dev/null
+++ b/dom/media/webrtc/transport/third_party/nrappkit/upstream.diff
@@ -0,0 +1,391 @@
+diff --git a/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h b/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/event/async_wait.h
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c b/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/log/r_log.c
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/port/darwin/include/csi_platform.h
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/port/linux/include/csi_platform.h
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h b/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/port/win32/include/csi_platform.h
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c b/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/registry/registry.c
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_crc32.c
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_macros.h
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr/r_time.h
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.c
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h b/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h
+--- a/dom/media/webrtc/transport/third_party/nrappkit/src/util/util.h
++++ b/dom/media/webrtc/transport/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/dom/media/webrtc/transport/transportflow.cpp b/dom/media/webrtc/transport/transportflow.cpp
new file mode 100644
index 0000000000..8b263c8cfe
--- /dev/null
+++ b/dom/media/webrtc/transport/transportflow.cpp
@@ -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
+#include <deque>
+
+#include "transportflow.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS0(TransportFlow)
+
+// There are some hacks here to allow destruction off of
+// the main thread.
+TransportFlow::~TransportFlow() {
+ // 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 a UniquePtr
+ // ensures automatic destruction of the queue at exit of
+ // DestroyFinal.
+ CheckThread();
+ ClearLayers(layers_.get());
+}
+
+void TransportFlow::DestroyFinal(
+ UniquePtr<std::deque<TransportLayer*>> layers) {
+ ClearLayers(layers.get());
+}
+
+void TransportFlow::ClearLayers(std::deque<TransportLayer*>* layers) {
+ while (!layers->empty()) {
+ delete layers->front();
+ layers->pop_front();
+ }
+}
+
+void TransportFlow::PushLayer(TransportLayer* layer) {
+ CheckThread();
+ layers_->push_front(layer);
+ EnsureSameThread(layer);
+ layer->SetFlowId(id_);
+}
+
+TransportLayer* TransportFlow::GetLayer(const std::string& id) const {
+ CheckThread();
+
+ if (layers_) {
+ for (TransportLayer* layer : *layers_) {
+ if (layer->id() == id) return layer;
+ }
+ }
+
+ return nullptr;
+}
+
+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();
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/transportflow.h b/dom/media/webrtc/transport/transportflow.h
new file mode 100644
index 0000000000..43253a260b
--- /dev/null
+++ b/dom/media/webrtc/transport/transportflow.h
@@ -0,0 +1,105 @@
+/* -*- 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 <string>
+
+#include "nscore.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/UniquePtr.h"
+#include "transportlayer.h"
+#include "m_cpp_utils.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:
+ TransportFlow()
+ : id_("(anonymous)"), layers_(new std::deque<TransportLayer*>) {}
+ explicit TransportFlow(const std::string id)
+ : id_(id), 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.
+ void PushLayer(TransportLayer* layer);
+
+ TransportLayer* GetLayer(const std::string& id) 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);
+
+ static void DestroyFinal(UniquePtr<std::deque<TransportLayer*>> layers);
+
+ // Overload needed because we use deque internally and queue externally.
+ static void ClearLayers(std::deque<TransportLayer*>* layers);
+
+ std::string id_;
+ UniquePtr<std::deque<TransportLayer*>> layers_;
+ nsCOMPtr<nsIEventTarget> target_;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/transportlayer.cpp b/dom/media/webrtc/transport/transportlayer.cpp
new file mode 100644
index 0000000000..8816f24754
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayer.cpp
@@ -0,0 +1,49 @@
+/* -*- 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 "transportlayer.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::Chain(TransportLayer* downward) {
+ downward_ = downward;
+ 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);
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/transportlayer.h b/dom/media/webrtc/transport/transportlayer.h
new file mode 100644
index 0000000000..47287ab4e0
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayer.h
@@ -0,0 +1,108 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+
+#include "m_cpp_utils.h"
+#include "mediapacket.h"
+
+namespace mozilla {
+
+class TransportFlow;
+
+typedef int TransportResult;
+
+enum { TE_WOULDBLOCK = -1, TE_ERROR = -2, TE_INTERNAL = -3 };
+
+#define TRANSPORT_LAYER_ID(name) \
+ const std::string id() const override { 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() = default;
+
+ // Called to initialize
+ nsresult Init(); // Called by Insert() to set up -- do not override
+ virtual nsresult InitInternal() { return NS_OK; } // Called by Init
+
+ void SetFlowId(const std::string& flow_id) { flow_id_ = flow_id; }
+
+ virtual void Chain(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(MediaPacket& packet) = 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::signal2<TransportLayer*, MediaPacket&> 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__)
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/transportlayerdtls.cpp b/dom/media/webrtc/transport/transportlayerdtls.cpp
new file mode 100644
index 0000000000..4ab8aaa029
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerdtls.cpp
@@ -0,0 +1,1558 @@
+/* -*- 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/Telemetry.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "sslexp.h"
+#include "sslproto.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)
+
+void TransportLayerNSPRAdapter::PacketReceived(MediaPacket& packet) {
+ if (enabled_) {
+ input_.push(new MediaPacket(std::move(packet)));
+ }
+}
+
+int32_t TransportLayerNSPRAdapter::Recv(void* buf, int32_t buflen) {
+ if (input_.empty()) {
+ PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
+ return -1;
+ }
+
+ MediaPacket* front = input_.front();
+ int32_t count = static_cast<int32_t>(front->len());
+
+ if (buflen < count) {
+ MOZ_ASSERT(false, "Not enough buffer space to receive into");
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
+ return -1;
+ }
+
+ memcpy(buf, front->data(), 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;
+ }
+
+ MediaPacket packet;
+ // Copies. Oh well.
+ packet.Copy(static_cast<const uint8_t*>(buf), static_cast<size_t>(length));
+ packet.SetType(MediaPacket::DTLS);
+
+ TransportResult r = output_->SendPacket(packet);
+ 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() {
+ // Destroy the NSS instance first so it can still send out an alert before
+ // we disable the nspr_io_adapter_.
+ ssl_fd_ = nullptr;
+ 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_ = NS_NewTimer();
+ if (!timer_) {
+ 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 DtlsDigest& digest) {
+ // Defensive programming
+ if (verification_mode_ != VERIFY_UNSET &&
+ verification_mode_ != VERIFY_DIGEST) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ digests_.push_back(digest);
+ verification_mode_ = VERIFY_DIGEST;
+ return NS_OK;
+}
+
+void TransportLayerDtls::SetMinMaxVersion(Version min_version,
+ Version max_version) {
+ if (min_version < Version::DTLS_1_0 || min_version > Version::DTLS_1_3 ||
+ max_version < Version::DTLS_1_0 || max_version > Version::DTLS_1_3 ||
+ min_version > max_version || max_version < min_version) {
+ return;
+ }
+ minVersion_ = min_version;
+ maxVersion_ = max_version;
+}
+
+// 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;
+ }
+
+ if (maxVersion_ >= Version::DTLS_1_3) {
+ MOZ_MTLOG(ML_INFO, "Setting DTLS1.3 supported_versions workaround");
+ rv = SSL_SetDtls13VersionWorkaround(ssl_fd.get(), PR_TRUE);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't set DTLS1.3 workaround");
+ 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().get(),
+ 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;
+ }
+ }
+
+ SSLVersionRange version_range = {static_cast<PRUint16>(minVersion_),
+ static_cast<PRUint16>(maxVersion_)};
+
+ 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_ = std::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 (const auto& tag : alpn_allowed_) {
+ 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_DHE_RSA_WITH_AES_128_CBC_SHA,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+
+ 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) {
+ SECStatus rv;
+
+ // Set the SRTP ciphers
+ if (!enabled_srtp_ciphers_.empty()) {
+ rv = SSL_InstallExtensionHooks(ssl_fd.get(), ssl_use_srtp_xtn,
+ TransportLayerDtls::WriteSrtpXtn, this,
+ TransportLayerDtls::HandleSrtpXtn, this);
+ if (rv != SECSuccess) {
+ MOZ_MTLOG(ML_ERROR, LAYER_INFO << "unable to set SRTP extension handler");
+ 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;
+}
+
+std::vector<uint16_t> TransportLayerDtls::GetDefaultSrtpCiphers() {
+ std::vector<uint16_t> ciphers;
+
+ ciphers.push_back(kDtlsSrtpAeadAes128Gcm);
+ // Since we don't support DTLS 1.3 or SHA384 ciphers (see bug 1312976)
+ // we don't really enough entropy to prefer this over 128 bit
+ ciphers.push_back(kDtlsSrtpAeadAes256Gcm);
+ ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_80);
+#ifndef NIGHTLY_BUILD
+ // To support bug 1491583 lets try to find out if we get bug reports if we no
+ // longer offer this in Nightly builds.
+ ciphers.push_back(kDtlsSrtpAes128CmHmacSha1_32);
+#endif
+
+ return ciphers;
+}
+
+void TransportLayerDtls::StateChange(TransportLayer* layer, State state) {
+ 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_INFO, LAYER_INFO << "Lower layer is connecting.");
+ break;
+
+ case TS_OPEN:
+ if (timer_) {
+ MOZ_MTLOG(ML_INFO,
+ LAYER_INFO << "Lower layer is now open; starting TLS");
+ timer_->Cancel();
+ timer_->SetTarget(target_);
+ // 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.
+ timer_->InitWithNamedFuncCallback(TimerCallback, this, 0,
+ nsITimer::TYPE_ONE_SHOT,
+ "TransportLayerDtls::TimerCallback");
+ TL_SET_STATE(TS_CONNECTING);
+ } else {
+ // We have already completed DTLS. Can happen if the ICE layer failed
+ // due to a loss of network, and then recovered.
+ TL_SET_STATE(TS_OPEN);
+ }
+ break;
+
+ case TS_CLOSED:
+ MOZ_MTLOG(ML_INFO, 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() {
+ if (!timer_) {
+ // We are done with DTLS, regardless of the state changes of lower layers
+ return;
+ }
+
+ // Clear the retransmit timer
+ timer_->Cancel();
+
+ MOZ_ASSERT(state_ == TS_CONNECTING);
+
+ 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);
+
+ RecordTlsTelemetry();
+ timer_ = nullptr;
+ } 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
+ [[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_->InitWithNamedFuncCallback(
+ TimerCallback, this, timeout_ms, nsITimer::TYPE_ONE_SHOT,
+ "TransportLayerDtls::TimerCallback");
+ }
+ 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,
+ MediaPacket& packet) {
+ CheckThread();
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << packet.len() << ")");
+
+ if (state_ != TS_CONNECTING && state_ != TS_OPEN) {
+ MOZ_MTLOG(ML_DEBUG,
+ LAYER_INFO << "Discarding packet in inappropriate state");
+ return;
+ }
+
+ if (!packet.data()) {
+ // Something ate this, probably the SRTP layer
+ return;
+ }
+
+ if (packet.type() != MediaPacket::DTLS) {
+ return;
+ }
+
+ nspr_io_adapter_->PacketReceived(packet);
+ GetDecryptedPackets();
+}
+
+void TransportLayerDtls::GetDecryptedPackets() {
+ // 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) {
+ int32_t rv;
+ // One packet might contain several DTLS packets
+ do {
+ // nICEr uses a 9216 bytes buffer to allow support for jumbo frames
+ // Can we peek to get a better idea of the actual size?
+ static const size_t kBufferSize = 9216;
+ auto buffer = MakeUnique<uint8_t[]>(kBufferSize);
+ rv = PR_Recv(ssl_fd_.get(), buffer.get(), kBufferSize, 0,
+ PR_INTERVAL_NO_WAIT);
+ if (rv > 0) {
+ // We have data
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS");
+ MediaPacket packet;
+ packet.SetType(MediaPacket::SCTP);
+ packet.Take(std::move(buffer), static_cast<size_t>(rv));
+ SignalPacketReceived(this, packet);
+ } 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);
+ }
+}
+
+void TransportLayerDtls::SetState(State state, const char* file,
+ unsigned line) {
+ if (timer_) {
+ switch (state) {
+ case TS_NONE:
+ case TS_INIT:
+ MOZ_ASSERT(false);
+ break;
+ case TS_CONNECTING:
+ break;
+ case TS_OPEN:
+ case TS_CLOSED:
+ case TS_ERROR:
+ timer_->Cancel();
+ break;
+ }
+ }
+
+ TransportLayer::SetState(state, file, line);
+}
+
+TransportResult TransportLayerDtls::SendPacket(MediaPacket& packet) {
+ 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(), packet.data(), packet.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().get());
+ if (!*pRetKey) {
+ CERT_DestroyCertificate(*pRetCert);
+ *pRetCert = nullptr;
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+nsresult TransportLayerDtls::SetSrtpCiphers(
+ const std::vector<uint16_t>& ciphers) {
+ enabled_srtp_ciphers_ = std::move(ciphers);
+ return NS_OK;
+}
+
+nsresult TransportLayerDtls::GetSrtpCipher(uint16_t* cipher) const {
+ CheckThread();
+ if (srtp_cipher_ == 0) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ *cipher = srtp_cipher_;
+ return NS_OK;
+}
+
+static uint8_t* WriteUint16(uint8_t* cursor, uint16_t v) {
+ *cursor++ = v >> 8;
+ *cursor++ = v & 0xff;
+ return cursor;
+}
+
+static SSLHandshakeType SrtpXtnServerMessage(PRFileDesc* fd) {
+ SSLPreliminaryChannelInfo preinfo;
+ SECStatus rv = SSL_GetPreliminaryChannelInfo(fd, &preinfo, sizeof(preinfo));
+ if (rv != SECSuccess) {
+ MOZ_ASSERT(false, "Can't get version info");
+ return ssl_hs_client_hello;
+ }
+ return (preinfo.protocolVersion >= SSL_LIBRARY_VERSION_TLS_1_3)
+ ? ssl_hs_encrypted_extensions
+ : ssl_hs_server_hello;
+}
+
+/* static */
+PRBool TransportLayerDtls::WriteSrtpXtn(PRFileDesc* fd,
+ SSLHandshakeType message, uint8_t* data,
+ unsigned int* len, unsigned int max_len,
+ void* arg) {
+ auto self = reinterpret_cast<TransportLayerDtls*>(arg);
+
+ // ClientHello: send all supported versions.
+ if (message == ssl_hs_client_hello) {
+ MOZ_ASSERT(self->role_ == CLIENT);
+ MOZ_ASSERT(self->enabled_srtp_ciphers_.size(), "Haven't enabled SRTP");
+ // We will take 2 octets for each cipher, plus a 2 octet length and 1 octet
+ // for the length of the empty MKI.
+ if (max_len < self->enabled_srtp_ciphers_.size() * 2 + 3) {
+ MOZ_ASSERT(false, "Not enough space to send SRTP extension");
+ return false;
+ }
+ uint8_t* cursor = WriteUint16(data, self->enabled_srtp_ciphers_.size() * 2);
+ for (auto cs : self->enabled_srtp_ciphers_) {
+ cursor = WriteUint16(cursor, cs);
+ }
+ *cursor++ = 0; // MKI is empty
+ *len = cursor - data;
+ return true;
+ }
+
+ if (message == SrtpXtnServerMessage(fd)) {
+ MOZ_ASSERT(self->role_ == SERVER);
+ if (!self->srtp_cipher_) {
+ // Not negotiated. Definitely bad, but the connection can fail later.
+ return false;
+ }
+ if (max_len < 5) {
+ MOZ_ASSERT(false, "Not enough space to send SRTP extension");
+ return false;
+ }
+
+ uint8_t* cursor = WriteUint16(data, 2); // Length = 2.
+ cursor = WriteUint16(cursor, self->srtp_cipher_);
+ *cursor++ = 0; // No MKI
+ *len = cursor - data;
+ return true;
+ }
+
+ return false;
+}
+
+class TlsParser {
+ public:
+ TlsParser(const uint8_t* data, size_t len) : cursor_(data), remaining_(len) {}
+
+ bool error() const { return error_; }
+ size_t remaining() const { return remaining_; }
+
+ template <typename T,
+ class = typename std::enable_if<std::is_unsigned<T>::value>::type>
+ void Read(T* v, size_t sz = sizeof(T)) {
+ MOZ_ASSERT(sz <= sizeof(T),
+ "Type is too small to hold the value requested");
+ if (remaining_ < sz) {
+ error_ = true;
+ return;
+ }
+
+ T result = 0;
+ for (size_t i = 0; i < sz; ++i) {
+ result = (result << 8) | *cursor_++;
+ remaining_--;
+ }
+ *v = result;
+ }
+
+ template <typename T,
+ class = typename std::enable_if<std::is_unsigned<T>::value>::type>
+ void ReadVector(std::vector<T>* v, size_t w) {
+ MOZ_ASSERT(v->empty(), "vector needs to be empty");
+
+ uint32_t len;
+ Read(&len, w);
+ if (error_ || len % sizeof(T) != 0 || len > remaining_) {
+ error_ = true;
+ return;
+ }
+
+ size_t count = len / sizeof(T);
+ v->reserve(count);
+ for (T i = 0; !error_ && i < count; ++i) {
+ T item;
+ Read(&item);
+ if (!error_) {
+ v->push_back(item);
+ }
+ }
+ }
+
+ void Skip(size_t n) {
+ if (remaining_ < n) {
+ error_ = true;
+ } else {
+ cursor_ += n;
+ remaining_ -= n;
+ }
+ }
+
+ size_t SkipVector(size_t w) {
+ uint32_t len = 0;
+ Read(&len, w);
+ Skip(len);
+ return len;
+ }
+
+ private:
+ const uint8_t* cursor_;
+ size_t remaining_;
+ bool error_ = false;
+};
+
+/* static */
+SECStatus TransportLayerDtls::HandleSrtpXtn(
+ PRFileDesc* fd, SSLHandshakeType message, const uint8_t* data,
+ unsigned int len, SSLAlertDescription* alert, void* arg) {
+ static const uint8_t kTlsAlertHandshakeFailure = 40;
+ static const uint8_t kTlsAlertIllegalParameter = 47;
+ static const uint8_t kTlsAlertDecodeError = 50;
+ static const uint8_t kTlsAlertUnsupportedExtension = 110;
+
+ auto self = reinterpret_cast<TransportLayerDtls*>(arg);
+
+ // Parse the extension.
+ TlsParser parser(data, len);
+ std::vector<uint16_t> advertised;
+ parser.ReadVector(&advertised, 2);
+ size_t mki_len = parser.SkipVector(1);
+ if (parser.error() || parser.remaining() > 0) {
+ *alert = kTlsAlertDecodeError;
+ return SECFailure;
+ }
+
+ if (message == ssl_hs_client_hello) {
+ MOZ_ASSERT(self->role_ == SERVER);
+ if (self->enabled_srtp_ciphers_.empty()) {
+ // We don't have SRTP enabled, which is probably bad, but no sense in
+ // having the handshake fail at this point, let the client decide if this
+ // is a problem.
+ return SECSuccess;
+ }
+
+ for (auto supported : self->enabled_srtp_ciphers_) {
+ auto it = std::find(advertised.begin(), advertised.end(), supported);
+ if (it != advertised.end()) {
+ self->srtp_cipher_ = supported;
+ return SECSuccess;
+ }
+ }
+
+ // No common cipher.
+ *alert = kTlsAlertHandshakeFailure;
+ return SECFailure;
+ }
+
+ if (message == SrtpXtnServerMessage(fd)) {
+ MOZ_ASSERT(self->role_ == CLIENT);
+ if (advertised.size() != 1 || mki_len > 0) {
+ *alert = kTlsAlertIllegalParameter;
+ return SECFailure;
+ }
+ self->srtp_cipher_ = advertised[0];
+ return SECSuccess;
+ }
+
+ *alert = kTlsAlertUnsupportedExtension;
+ return SECFailure;
+}
+
+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 DtlsDigest& digest, UniqueCERTCertificate& peer_cert) const {
+ DtlsDigest computed_digest(digest.algorithm_);
+
+ MOZ_MTLOG(ML_DEBUG,
+ LAYER_INFO << "Checking digest, algorithm=" << digest.algorithm_);
+ nsresult res = DtlsIdentity::ComputeFingerprint(peer_cert, &computed_digest);
+ 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 != digest) {
+ 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_.empty());
+ // Check all the provided digests
+
+ // Checking functions call PR_SetError()
+ SECStatus rv = SECFailure;
+ for (auto digest : digests_) {
+ 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();
+}
+
+void TransportLayerDtls::RecordTlsTelemetry() {
+ MOZ_ASSERT(state_ == TS_OPEN);
+ SSLChannelInfo info;
+ SECStatus ss = SSL_GetChannelInfo(ssl_fd_.get(), &info, sizeof(info));
+ if (ss != SECSuccess) {
+ MOZ_MTLOG(ML_NOTICE,
+ LAYER_INFO << "RecordTlsTelemetry failed to get channel info");
+ return;
+ }
+
+ uint16_t telemetry_cipher = 0;
+
+ switch (info.cipherSuite) {
+ /* Old DHE ciphers: candidates for removal, see bug 1227519 */
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ telemetry_cipher = 1;
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ telemetry_cipher = 2;
+ break;
+ /* Current ciphers */
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ telemetry_cipher = 3;
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ telemetry_cipher = 4;
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ telemetry_cipher = 5;
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ telemetry_cipher = 6;
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ telemetry_cipher = 7;
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ telemetry_cipher = 8;
+ break;
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ telemetry_cipher = 9;
+ break;
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ telemetry_cipher = 10;
+ break;
+ /* TLS 1.3 ciphers */
+ case TLS_AES_128_GCM_SHA256:
+ telemetry_cipher = 11;
+ break;
+ case TLS_CHACHA20_POLY1305_SHA256:
+ telemetry_cipher = 12;
+ break;
+ case TLS_AES_256_GCM_SHA384:
+ telemetry_cipher = 13;
+ break;
+ }
+
+ Telemetry::Accumulate(Telemetry::WEBRTC_DTLS_CIPHER, telemetry_cipher);
+
+ uint16_t cipher;
+ nsresult rv = GetSrtpCipher(&cipher);
+
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_DEBUG, "No SRTP cipher suite");
+ return;
+ }
+
+ auto cipher_label = mozilla::Telemetry::LABELS_WEBRTC_SRTP_CIPHER::Unknown;
+
+ switch (cipher) {
+ case kDtlsSrtpAes128CmHmacSha1_80:
+ cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::Aes128CmHmacSha1_80;
+ break;
+ case kDtlsSrtpAes128CmHmacSha1_32:
+ cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::Aes128CmHmacSha1_32;
+ break;
+ case kDtlsSrtpAeadAes128Gcm:
+ cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::AeadAes128Gcm;
+ break;
+ case kDtlsSrtpAeadAes256Gcm:
+ cipher_label = Telemetry::LABELS_WEBRTC_SRTP_CIPHER::AeadAes256Gcm;
+ break;
+ }
+
+ Telemetry::AccumulateCategorical(cipher_label);
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/transportlayerdtls.h b/dom/media/webrtc/transport/transportlayerdtls.h
new file mode 100644
index 0000000000..078b266e8b
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerdtls.h
@@ -0,0 +1,187 @@
+/* -*- 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>
+
+#ifdef XP_MACOSX
+// ensure that Apple Security kit enum goes before "sslproto.h"
+# include <CoreFoundation/CFAvailability.h>
+# include <Security/CipherSuite.h>
+#endif
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsITimer.h"
+#include "ScopedNSSTypes.h"
+#include "m_cpp_utils.h"
+#include "dtlsidentity.h"
+#include "transportlayer.h"
+#include "ssl.h"
+#include "sslproto.h"
+
+namespace mozilla {
+
+// RFC 5764 (we don't support the NULL cipher)
+static const uint16_t kDtlsSrtpAes128CmHmacSha1_80 = 0x0001;
+static const uint16_t kDtlsSrtpAes128CmHmacSha1_32 = 0x0002;
+// RFC 7714
+static const uint16_t kDtlsSrtpAeadAes128Gcm = 0x0007;
+static const uint16_t kDtlsSrtpAeadAes256Gcm = 0x0008;
+
+struct Packet;
+
+class TransportLayerNSPRAdapter {
+ public:
+ explicit TransportLayerNSPRAdapter(TransportLayer* output)
+ : output_(output), input_(), enabled_(true) {}
+
+ void PacketReceived(MediaPacket& packet);
+ 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<MediaPacket*> input_;
+ bool enabled_;
+};
+
+class TransportLayerDtls final : public TransportLayer {
+ public:
+ TransportLayerDtls() = default;
+
+ virtual ~TransportLayerDtls();
+
+ enum Role { CLIENT, SERVER };
+ enum Verification { VERIFY_UNSET, VERIFY_ALLOW_ALL, VERIFY_DIGEST };
+
+ // DTLS-specific operations
+ void SetRole(Role role) { role_ = role; }
+ Role role() { return role_; }
+
+ enum class Version : uint16_t {
+ DTLS_1_0 = SSL_LIBRARY_VERSION_DTLS_1_0,
+ DTLS_1_2 = SSL_LIBRARY_VERSION_DTLS_1_2,
+ DTLS_1_3 = SSL_LIBRARY_VERSION_DTLS_1_3
+ };
+ void SetMinMaxVersion(Version min_version, Version max_version);
+
+ 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 DtlsDigest& digest);
+
+ nsresult GetCipherSuite(uint16_t* cipherSuite) const;
+
+ nsresult SetSrtpCiphers(const std::vector<uint16_t>& ciphers);
+ nsresult GetSrtpCipher(uint16_t* cipher) const;
+ static std::vector<uint16_t> GetDefaultSrtpCiphers();
+
+ nsresult ExportKeyingMaterial(const std::string& label, bool use_context,
+ const std::string& context, unsigned char* out,
+ unsigned int outlen);
+
+ // Transport layer overrides.
+ nsresult InitInternal() override;
+ void WasInserted() override;
+ TransportResult SendPacket(MediaPacket& packet) override;
+
+ // Signals
+ void StateChange(TransportLayer* layer, State state);
+ void PacketReceived(TransportLayer* layer, MediaPacket& packet);
+
+ // For testing use only. Returns the fd.
+ PRFileDesc* internal_fd() {
+ CheckThread();
+ return ssl_fd_.get();
+ }
+
+ TRANSPORT_LAYER_ID("dtls")
+
+ protected:
+ void SetState(State state, const char* file, unsigned line) override;
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerDtls);
+
+ bool Setup();
+ bool SetupCipherSuites(UniquePRFileDesc& ssl_fd);
+ bool SetupAlpn(UniquePRFileDesc& ssl_fd) const;
+ void GetDecryptedPackets();
+ 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 DtlsDigest& digest,
+ UniqueCERTCertificate& cert) const;
+
+ void RecordHandshakeCompletionTelemetry(TransportLayer::State endState);
+ void RecordTlsTelemetry();
+
+ static PRBool WriteSrtpXtn(PRFileDesc* fd, SSLHandshakeType message,
+ uint8_t* data, unsigned int* len,
+ unsigned int max_len, void* arg);
+
+ static SECStatus HandleSrtpXtn(PRFileDesc* fd, SSLHandshakeType message,
+ const uint8_t* data, unsigned int len,
+ SSLAlertDescription* alert, void* arg);
+
+ 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> enabled_srtp_ciphers_;
+ uint16_t srtp_cipher_ = 0;
+
+ Role role_ = CLIENT;
+ Verification verification_mode_ = VERIFY_UNSET;
+ std::vector<DtlsDigest> digests_;
+
+ Version minVersion_ = Version::DTLS_1_0;
+ Version maxVersion_ = Version::DTLS_1_2;
+
+ // 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_ = nullptr;
+ UniquePRFileDesc ssl_fd_ = nullptr;
+
+ nsCOMPtr<nsITimer> timer_ = nullptr;
+ bool auth_hook_called_ = false;
+ bool cert_ok_ = false;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/transportlayerice.cpp b/dom/media/webrtc/transport/transportlayerice.cpp
new file mode 100644
index 0000000000..03d8131ead
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerice.cpp
@@ -0,0 +1,168 @@
+/* -*- 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 "nsComponentManagerUtils.h"
+#include "nsError.h"
+#include "nsNetCID.h"
+
+// nICEr includes
+extern "C" {
+#include "nr_api.h"
+#include "ice_util.h"
+#include "transport_addr.h"
+}
+
+// Local includes
+#include "logging.h"
+#include "nricemediastream.h"
+#include "transportlayerice.h"
+
+namespace mozilla {
+
+#ifdef ERROR
+# undef ERROR
+#endif
+
+MOZ_MTLOG_MODULE("mtransport")
+
+TransportLayerIce::TransportLayerIce() : stream_(nullptr), component_(0) {
+ // setup happens later
+}
+
+TransportLayerIce::~TransportLayerIce() {
+ // No need to do anything here, since we use smart pointers
+}
+
+void TransportLayerIce::SetParameters(RefPtr<NrIceMediaStream> stream,
+ int component) {
+ // Stream could be null in the case of some badly written js that causes
+ // us to be in an ICE restart case, but not have valid streams due to
+ // not calling PeerConnectionImpl::EnsureTransports if
+ // PeerConnectionImpl::SetSignalingState_m thinks the conditions were
+ // not correct. We also solved a case where an incoming answer was
+ // incorrectly beginning an ICE restart when the offer did not indicate one.
+ if (!stream) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ stream_ = stream;
+ component_ = component;
+
+ PostSetup();
+}
+
+void TransportLayerIce::PostSetup() {
+ 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);
+ }
+}
+
+TransportResult TransportLayerIce::SendPacket(MediaPacket& packet) {
+ CheckThread();
+ SignalPacketSending(this, packet);
+ nsresult res = stream_->SendPacket(component_, packet.data(), packet.len());
+
+ if (!NS_SUCCEEDED(res)) {
+ return (res == NS_BASE_STREAM_WOULD_BLOCK) ? TE_WOULDBLOCK : TE_ERROR;
+ }
+
+ MOZ_MTLOG(ML_DEBUG,
+ LAYER_INFO << " SendPacket(" << packet.len() << ") succeeded");
+
+ return packet.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 << ")");
+ // Might be useful to allow MediaPacket to borrow a buffer (ie; not take
+ // ownership, but copy it if the MediaPacket is moved). This could be a
+ // footgun though with MediaPackets that end up on the heap.
+ MediaPacket packet;
+ packet.Copy(data, len);
+ packet.Categorize();
+
+ SignalPacketReceived(this, packet);
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/transportlayerice.h b/dom/media/webrtc/transport/transportlayerice.h
new file mode 100644
index 0000000000..1c55b71ce8
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerice.h
@@ -0,0 +1,60 @@
+/* -*- 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 "sigslot.h"
+
+#include "mozilla/RefPtr.h"
+
+#include "m_cpp_utils.h"
+
+#include "nricemediastream.h"
+#include "transportlayer.h"
+
+// An ICE transport layer -- corresponds to a single ICE
+namespace mozilla {
+
+class TransportLayerIce : public TransportLayer {
+ public:
+ TransportLayerIce();
+
+ virtual ~TransportLayerIce();
+
+ void SetParameters(RefPtr<NrIceMediaStream> stream, int component);
+
+ void ResetOldStream(); // called after successful ice restart
+ void RestoreOldStream(); // called after unsuccessful ice restart
+
+ // Transport layer overrides.
+ TransportResult SendPacket(MediaPacket& packet) override;
+
+ // 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);
+
+ // Useful for capturing encrypted packets
+ sigslot::signal2<TransportLayer*, MediaPacket&> SignalPacketSending;
+
+ TRANSPORT_LAYER_ID("ice")
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerIce);
+ void PostSetup();
+
+ RefPtr<NrIceMediaStream> stream_;
+ int component_;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/transportlayerlog.cpp b/dom/media/webrtc/transport/transportlayerlog.cpp
new file mode 100644
index 0000000000..d78e722dca
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerlog.cpp
@@ -0,0 +1,48 @@
+/* -*- 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 "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(MediaPacket& packet) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << packet.len() << ")");
+
+ if (downward_) {
+ return downward_->SendPacket(packet);
+ }
+ return static_cast<TransportResult>(packet.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,
+ MediaPacket& packet) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << packet.len() << ")");
+
+ SignalPacketReceived(this, packet);
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/transportlayerlog.h b/dom/media/webrtc/transport/transportlayerlog.h
new file mode 100644
index 0000000000..4d0b3fd930
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerlog.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 transportlayerlog_h__
+#define transportlayerlog_h__
+
+#include "m_cpp_utils.h"
+#include "transportlayer.h"
+
+namespace mozilla {
+
+class TransportLayerLogging : public TransportLayer {
+ public:
+ TransportLayerLogging() = default;
+
+ // Overrides for TransportLayer
+ TransportResult SendPacket(MediaPacket& packet) override;
+
+ // Signals (forwarded to upper layer)
+ void StateChange(TransportLayer* layer, State state);
+ void PacketReceived(TransportLayer* layer, MediaPacket& packet);
+
+ TRANSPORT_LAYER_ID("log")
+
+ protected:
+ void WasInserted() override;
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerLogging);
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/transportlayerloopback.cpp b/dom/media/webrtc/transport/transportlayerloopback.cpp
new file mode 100644
index 0000000000..ab2a52c17e
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerloopback.cpp
@@ -0,0 +1,119 @@
+/* -*- 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 "prlock.h"
+
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+
+#include "transportlayerloopback.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+nsresult TransportLayerLoopback::Init() {
+ nsresult rv;
+ target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (!NS_SUCCEEDED(rv)) return rv;
+
+ timer_ = NS_NewTimer(target_);
+ MOZ_ASSERT(timer_);
+ if (!timer_) return NS_ERROR_FAILURE;
+
+ 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(MediaPacket& packet) {
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "SendPacket(" << packet.len() << ")");
+
+ if (!peer_) {
+ MOZ_MTLOG(ML_ERROR, "Discarding packet because peer not attached");
+ return TE_ERROR;
+ }
+
+ size_t len = packet.len();
+ nsresult res = peer_->QueuePacket(packet);
+ if (!NS_SUCCEEDED(res)) return TE_ERROR;
+
+ return static_cast<TransportResult>(len);
+}
+
+nsresult TransportLayerLoopback::QueuePacket(MediaPacket& packet) {
+ MOZ_ASSERT(packets_lock_);
+
+ PR_Lock(packets_lock_);
+
+ if (combinePackets_ && !packets_.empty()) {
+ MediaPacket* prevPacket = packets_.front();
+
+ MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " Enqueuing combined packets of length "
+ << prevPacket->len() << " and "
+ << packet.len());
+ auto combined = MakeUnique<uint8_t[]>(prevPacket->len() + packet.len());
+ memcpy(combined.get(), prevPacket->data(), prevPacket->len());
+ memcpy(combined.get() + prevPacket->len(), packet.data(), packet.len());
+ prevPacket->Take(std::move(combined), prevPacket->len() + packet.len());
+ } else {
+ MOZ_MTLOG(ML_DEBUG,
+ LAYER_INFO << " Enqueuing packet of length " << packet.len());
+ packets_.push(new MediaPacket(std::move(packet)));
+ }
+
+ 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()) {
+ UniquePtr<MediaPacket> packet(packets_.front());
+ packets_.pop();
+
+ MOZ_MTLOG(ML_DEBUG,
+ LAYER_INFO << " Delivering packet of length " << packet->len());
+ SignalPacketReceived(this, *packet);
+ }
+}
+
+NS_IMPL_ISUPPORTS(TransportLayerLoopback::Deliverer, nsITimerCallback, nsINamed)
+
+NS_IMETHODIMP TransportLayerLoopback::Deliverer::Notify(nsITimer* timer) {
+ if (!layer_) return NS_OK;
+
+ layer_->DeliverPackets();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP TransportLayerLoopback::Deliverer::GetName(nsACString& aName) {
+ aName.AssignLiteral("TransportLayerLoopback::Deliverer");
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/transportlayerloopback.h b/dom/media/webrtc/transport/transportlayerloopback.h
new file mode 100644
index 0000000000..6801af8189
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayerloopback.h
@@ -0,0 +1,109 @@
+/* -*- 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 "prlock.h"
+
+#include <queue>
+
+#include "nsCOMPtr.h"
+#include "nsINamed.h"
+#include "nsITimer.h"
+
+#include "m_cpp_utils.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()) {
+ MediaPacket* 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
+ TransportResult SendPacket(MediaPacket& packet) override;
+
+ // Deliver queued packets
+ void DeliverPackets();
+
+ TRANSPORT_LAYER_ID("loopback")
+
+ private:
+ DISALLOW_COPY_ASSIGN(TransportLayerLoopback);
+
+ // A timer to deliver packets if some are available
+ // Fires every 100 ms
+ class Deliverer : public nsITimerCallback, public nsINamed {
+ public:
+ explicit Deliverer(TransportLayerLoopback* layer) : layer_(layer) {}
+ void Detach() { layer_ = nullptr; }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ private:
+ virtual ~Deliverer() = default;
+
+ DISALLOW_COPY_ASSIGN(Deliverer);
+
+ TransportLayerLoopback* layer_;
+ };
+
+ // Queue a packet for delivery
+ nsresult QueuePacket(MediaPacket& packet);
+
+ TransportLayerLoopback* peer_;
+ nsCOMPtr<nsITimer> timer_;
+ std::queue<MediaPacket*> packets_;
+ PRLock* packets_lock_;
+ RefPtr<Deliverer> deliverer_;
+ bool combinePackets_;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transport/transportlayersrtp.cpp b/dom/media/webrtc/transport/transportlayersrtp.cpp
new file mode 100644
index 0000000000..25830a2dde
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayersrtp.cpp
@@ -0,0 +1,222 @@
+/* -*- 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 "transportlayersrtp.h"
+#include "transportlayerdtls.h"
+
+#include "logging.h"
+#include "nsError.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+
+MOZ_MTLOG_MODULE("mtransport")
+
+static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp";
+
+TransportLayerSrtp::TransportLayerSrtp(TransportLayerDtls& dtls) {
+ // We need to connect to the dtls layer, not the ice layer, because even
+ // though the packets that DTLS decrypts don't flow through us, we do base our
+ // keying information on the keying information established by the DTLS layer.
+ dtls.SignalStateChange.connect(this, &TransportLayerSrtp::StateChange);
+
+ TL_SET_STATE(dtls.state());
+}
+
+void TransportLayerSrtp::WasInserted() {
+ // Connect to the lower layers
+ if (!Setup()) {
+ TL_SET_STATE(TS_ERROR);
+ }
+}
+
+bool TransportLayerSrtp::Setup() {
+ CheckThread();
+ if (!downward_) {
+ MOZ_MTLOG(ML_ERROR, "SRTP layer with nothing below. This is useless");
+ return false;
+ }
+
+ // downward_ is the TransportLayerIce
+ downward_->SignalPacketReceived.connect(this,
+ &TransportLayerSrtp::PacketReceived);
+
+ return true;
+}
+
+TransportResult TransportLayerSrtp::SendPacket(MediaPacket& packet) {
+ if (state() != TS_OPEN) {
+ return TE_ERROR;
+ }
+
+ if (packet.len() < 4) {
+ MOZ_ASSERT(false);
+ return TE_ERROR;
+ }
+
+ MOZ_ASSERT(packet.capacity() - packet.len() >= SRTP_MAX_EXPANSION);
+
+ int out_len;
+ nsresult res;
+ switch (packet.type()) {
+ case MediaPacket::RTP:
+ res = mSendSrtp->ProtectRtp(packet.data(), packet.len(),
+ packet.capacity(), &out_len);
+ packet.SetType(MediaPacket::SRTP);
+ break;
+ case MediaPacket::RTCP:
+ res = mSendSrtp->ProtectRtcp(packet.data(), packet.len(),
+ packet.capacity(), &out_len);
+ packet.SetType(MediaPacket::SRTCP);
+ break;
+ default:
+ MOZ_CRASH("SRTP layer asked to send packet that is neither RTP or RTCP");
+ }
+
+ if (NS_FAILED(res)) {
+ MOZ_MTLOG(ML_ERROR,
+ "Error protecting "
+ << (packet.type() == MediaPacket::RTP ? "RTP" : "RTCP")
+ << " len=" << packet.len() << "[" << std::hex
+ << packet.data()[0] << " " << packet.data()[1] << " "
+ << packet.data()[2] << " " << packet.data()[3] << "]");
+ return TE_ERROR;
+ }
+
+ size_t unencrypted_len = packet.len();
+ packet.SetLength(out_len);
+
+ TransportResult bytes = downward_->SendPacket(packet);
+ if (bytes == out_len) {
+ // Whole packet was written, but the encrypted length might be different.
+ // Don't confuse the caller.
+ return unencrypted_len;
+ }
+
+ if (bytes == TE_WOULDBLOCK) {
+ return TE_WOULDBLOCK;
+ }
+
+ return TE_ERROR;
+}
+
+void TransportLayerSrtp::StateChange(TransportLayer* layer, State state) {
+ if (state == TS_OPEN && !mSendSrtp) {
+ TransportLayerDtls* dtls = static_cast<TransportLayerDtls*>(layer);
+ MOZ_ASSERT(dtls); // DTLS is mandatory
+
+ uint16_t cipher_suite;
+ nsresult res = dtls->GetSrtpCipher(&cipher_suite);
+ if (NS_FAILED(res)) {
+ MOZ_MTLOG(ML_DEBUG, "DTLS-SRTP disabled");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ unsigned int key_size = SrtpFlow::KeySize(cipher_suite);
+ unsigned int salt_size = SrtpFlow::SaltSize(cipher_suite);
+ unsigned int master_key_size = key_size + salt_size;
+ MOZ_ASSERT(master_key_size <= SRTP_MAX_KEY_LENGTH);
+
+ // SRTP Key Exporter as per RFC 5764 S 4.2
+ unsigned char srtp_block[SRTP_MAX_KEY_LENGTH * 2];
+ res = dtls->ExportKeyingMaterial(kDTLSExporterLabel, false, "", srtp_block,
+ sizeof(srtp_block));
+ if (NS_FAILED(res)) {
+ MOZ_MTLOG(ML_ERROR, "Failed to compute DTLS-SRTP keys. This is an error");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ // Slice and dice as per RFC 5764 S 4.2
+ unsigned char client_write_key[SRTP_MAX_KEY_LENGTH];
+ unsigned char server_write_key[SRTP_MAX_KEY_LENGTH];
+ unsigned int offset = 0;
+ memcpy(client_write_key, srtp_block + offset, key_size);
+ offset += key_size;
+ memcpy(server_write_key, srtp_block + offset, key_size);
+ offset += key_size;
+ memcpy(client_write_key + key_size, srtp_block + offset, salt_size);
+ offset += salt_size;
+ memcpy(server_write_key + key_size, srtp_block + offset, salt_size);
+ MOZ_ASSERT((offset + salt_size) == (2 * master_key_size));
+
+ unsigned char* write_key;
+ unsigned char* read_key;
+
+ if (dtls->role() == TransportLayerDtls::CLIENT) {
+ write_key = client_write_key;
+ read_key = server_write_key;
+ } else {
+ write_key = server_write_key;
+ read_key = client_write_key;
+ }
+
+ MOZ_ASSERT(!mSendSrtp && !mRecvSrtp);
+ mSendSrtp =
+ SrtpFlow::Create(cipher_suite, false, write_key, master_key_size);
+ mRecvSrtp = SrtpFlow::Create(cipher_suite, true, read_key, master_key_size);
+ if (!mSendSrtp || !mRecvSrtp) {
+ MOZ_MTLOG(ML_ERROR, "Couldn't create SRTP flow.");
+ TL_SET_STATE(TS_ERROR);
+ return;
+ }
+
+ MOZ_MTLOG(ML_INFO, "Created SRTP flow!");
+ }
+
+ TL_SET_STATE(state);
+}
+
+void TransportLayerSrtp::PacketReceived(TransportLayer* layer,
+ MediaPacket& packet) {
+ if (state() != TS_OPEN) {
+ return;
+ }
+
+ if (!packet.data()) {
+ // Something ate this, probably the DTLS layer
+ return;
+ }
+
+ if (packet.type() != MediaPacket::SRTP &&
+ packet.type() != MediaPacket::SRTCP) {
+ return;
+ }
+
+ // We want to keep the encrypted packet around for packet dumping
+ packet.CopyDataToEncrypted();
+ int outLen;
+ nsresult res;
+
+ if (packet.type() == MediaPacket::SRTP) {
+ packet.SetType(MediaPacket::RTP);
+ res = mRecvSrtp->UnprotectRtp(packet.data(), packet.len(), packet.len(),
+ &outLen);
+ } else {
+ packet.SetType(MediaPacket::RTCP);
+ res = mRecvSrtp->UnprotectRtcp(packet.data(), packet.len(), packet.len(),
+ &outLen);
+ }
+
+ if (NS_SUCCEEDED(res)) {
+ packet.SetLength(outLen);
+ SignalPacketReceived(this, packet);
+ } else {
+ // TODO: What do we do wrt packet dumping here? Maybe signal an empty
+ // packet? Signal the still-encrypted packet?
+ MOZ_MTLOG(ML_ERROR,
+ "Error unprotecting "
+ << (packet.type() == MediaPacket::RTP ? "RTP" : "RTCP")
+ << " len=" << packet.len() << "[" << std::hex
+ << packet.data()[0] << " " << packet.data()[1] << " "
+ << packet.data()[2] << " " << packet.data()[3] << "]");
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/transportlayersrtp.h b/dom/media/webrtc/transport/transportlayersrtp.h
new file mode 100644
index 0000000000..a5ba035930
--- /dev/null
+++ b/dom/media/webrtc/transport/transportlayersrtp.h
@@ -0,0 +1,43 @@
+/* -*- 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 transportlayersrtp_h__
+#define transportlayersrtp_h__
+
+#include <string>
+
+#include "transportlayer.h"
+#include "mozilla/RefPtr.h"
+#include "SrtpFlow.h"
+
+namespace mozilla {
+
+class TransportLayerDtls;
+
+class TransportLayerSrtp final : public TransportLayer {
+ public:
+ explicit TransportLayerSrtp(TransportLayerDtls& dtls);
+ virtual ~TransportLayerSrtp() = default;
+
+ // Transport layer overrides.
+ void WasInserted() override;
+ TransportResult SendPacket(MediaPacket& packet) override;
+
+ // Signals
+ void StateChange(TransportLayer* layer, State state);
+ void PacketReceived(TransportLayer* layer, MediaPacket& packet);
+
+ TRANSPORT_LAYER_ID("srtp")
+
+ private:
+ bool Setup();
+ DISALLOW_COPY_ASSIGN(TransportLayerSrtp);
+ RefPtr<SrtpFlow> mSendSrtp;
+ RefPtr<SrtpFlow> mRecvSrtp;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transportbridge/MediaPipeline.cpp b/dom/media/webrtc/transportbridge/MediaPipeline.cpp
new file mode 100644
index 0000000000..6cb0afc36e
--- /dev/null
+++ b/dom/media/webrtc/transportbridge/MediaPipeline.cpp
@@ -0,0 +1,1682 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+// Original author: ekr@rtfm.com
+
+#include "MediaPipeline.h"
+
+#include <inttypes.h>
+#include <math.h>
+#include <sstream>
+
+#include "AudioSegment.h"
+#include "AudioConverter.h"
+#include "DOMMediaStream.h"
+#include "ImageContainer.h"
+#include "ImageTypes.h"
+#include "MediaEngine.h"
+#include "MediaSegment.h"
+#include "MediaTrackGraphImpl.h"
+#include "MediaTrackListener.h"
+#include "MediaStreamTrack.h"
+#include "jsapi/RemoteTrackSource.h"
+#include "RtpLogger.h"
+#include "VideoFrameConverter.h"
+#include "VideoSegment.h"
+#include "VideoStreamTrack.h"
+#include "VideoUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PeerIdentity.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/TaskQueue.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/dom/RTCStatsReportBinding.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/gfx/Types.h"
+#include "nsError.h"
+#include "nsThreadUtils.h"
+#include "transport/runnable_utils.h"
+#include "jsapi/MediaTransportHandler.h"
+#include "jsapi/PeerConnectionImpl.h"
+#include "Tracing.h"
+#include "libwebrtcglue/WebrtcImageBuffer.h"
+#include "common_video/include/video_frame_buffer.h"
+#include "modules/rtp_rtcp/include/rtp_rtcp.h"
+#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
+#include "modules/rtp_rtcp/source/rtp_packet_received.h"
+
+// Max size given stereo is 480*2*2 = 1920 (10ms of 16-bits stereo audio at
+// 48KHz)
+#define AUDIO_SAMPLE_BUFFER_MAX_BYTES (480 * 2 * 2)
+static_assert((WEBRTC_MAX_SAMPLE_RATE / 100) * sizeof(uint16_t) * 2 <=
+ AUDIO_SAMPLE_BUFFER_MAX_BYTES,
+ "AUDIO_SAMPLE_BUFFER_MAX_BYTES is not large enough");
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+mozilla::LazyLogModule gMediaPipelineLog("MediaPipeline");
+
+namespace mozilla {
+
+// An async inserter for audio data, to avoid running audio codec encoders
+// on the MTG/input audio thread. Basically just bounces all the audio
+// data to a single audio processing/input queue. We could if we wanted to
+// use multiple threads and a TaskQueue.
+class AudioProxyThread {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioProxyThread)
+
+ explicit AudioProxyThread(RefPtr<AudioSessionConduit> aConduit)
+ : mConduit(std::move(aConduit)),
+ mTaskQueue(TaskQueue::Create(
+ GetMediaThreadPool(MediaThreadType::WEBRTC_WORKER), "AudioProxy")),
+ mAudioConverter(nullptr) {
+ MOZ_ASSERT(mConduit);
+ MOZ_COUNT_CTOR(AudioProxyThread);
+ }
+
+ // This function is the identity if aInputRate is supported.
+ // Else, it returns a rate that is supported, that ensure no loss in audio
+ // quality: the sampling rate returned is always greater to the inputed
+ // sampling-rate, if they differ..
+ uint32_t AppropriateSendingRateForInputRate(uint32_t aInputRate) {
+ AudioSessionConduit* conduit =
+ static_cast<AudioSessionConduit*>(mConduit.get());
+ if (conduit->IsSamplingFreqSupported(aInputRate)) {
+ return aInputRate;
+ }
+ if (aInputRate < 16000) {
+ return 16000;
+ }
+ if (aInputRate < 32000) {
+ return 32000;
+ }
+ if (aInputRate < 44100) {
+ return 44100;
+ }
+ return 48000;
+ }
+
+ // From an arbitrary AudioChunk at sampling-rate aRate, process the audio into
+ // something the conduit can work with (or send silence if the track is not
+ // enabled), and send the audio in 10ms chunks to the conduit.
+ void InternalProcessAudioChunk(TrackRate aRate, const AudioChunk& aChunk,
+ bool aEnabled) {
+ MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
+ // Convert to interleaved 16-bits integer audio, with a maximum of two
+ // channels (since the WebRTC.org code below makes the assumption that the
+ // input audio is either mono or stereo), with a sample-rate rate that is
+ // 16, 32, 44.1, or 48kHz.
+ uint32_t outputChannels = aChunk.ChannelCount() == 1 ? 1 : 2;
+ int32_t transmissionRate = AppropriateSendingRateForInputRate(aRate);
+
+ // We take advantage of the fact that the common case (microphone directly
+ // to PeerConnection, that is, a normal call), the samples are already
+ // 16-bits mono, so the representation in interleaved and planar is the
+ // same, and we can just use that.
+ if (aEnabled && outputChannels == 1 &&
+ aChunk.mBufferFormat == AUDIO_FORMAT_S16 && transmissionRate == aRate) {
+ const int16_t* samples = aChunk.ChannelData<int16_t>().Elements()[0];
+ PacketizeAndSend(samples, transmissionRate, outputChannels,
+ aChunk.mDuration);
+ return;
+ }
+
+ uint32_t sampleCount = aChunk.mDuration * outputChannels;
+ if (mInterleavedAudio.Length() < sampleCount) {
+ mInterleavedAudio.SetLength(sampleCount);
+ }
+
+ if (!aEnabled || aChunk.mBufferFormat == AUDIO_FORMAT_SILENCE) {
+ PodZero(mInterleavedAudio.Elements(), sampleCount);
+ } else if (aChunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
+ DownmixAndInterleave(aChunk.ChannelData<float>(), aChunk.mDuration,
+ aChunk.mVolume, outputChannels,
+ mInterleavedAudio.Elements());
+ } else if (aChunk.mBufferFormat == AUDIO_FORMAT_S16) {
+ DownmixAndInterleave(aChunk.ChannelData<int16_t>(), aChunk.mDuration,
+ aChunk.mVolume, outputChannels,
+ mInterleavedAudio.Elements());
+ }
+ int16_t* inputAudio = mInterleavedAudio.Elements();
+ size_t inputAudioFrameCount = aChunk.mDuration;
+
+ AudioConfig inputConfig(AudioConfig::ChannelLayout(outputChannels), aRate,
+ AudioConfig::FORMAT_S16);
+ AudioConfig outputConfig(AudioConfig::ChannelLayout(outputChannels),
+ transmissionRate, AudioConfig::FORMAT_S16);
+ // Resample to an acceptable sample-rate for the sending side
+ if (!mAudioConverter || mAudioConverter->InputConfig() != inputConfig ||
+ mAudioConverter->OutputConfig() != outputConfig) {
+ mAudioConverter = MakeUnique<AudioConverter>(inputConfig, outputConfig);
+ }
+
+ int16_t* processedAudio = nullptr;
+ size_t framesProcessed =
+ mAudioConverter->Process(inputAudio, inputAudioFrameCount);
+
+ if (framesProcessed == 0) {
+ // In place conversion not possible, use a buffer.
+ framesProcessed = mAudioConverter->Process(mOutputAudio, inputAudio,
+ inputAudioFrameCount);
+ processedAudio = mOutputAudio.Data();
+ } else {
+ processedAudio = inputAudio;
+ }
+
+ PacketizeAndSend(processedAudio, transmissionRate, outputChannels,
+ framesProcessed);
+ }
+
+ // This packetizes aAudioData in 10ms chunks and sends it.
+ // aAudioData is interleaved audio data at a rate and with a channel count
+ // that is appropriate to send with the conduit.
+ void PacketizeAndSend(const int16_t* aAudioData, uint32_t aRate,
+ uint32_t aChannels, uint32_t aFrameCount) {
+ MOZ_ASSERT(AppropriateSendingRateForInputRate(aRate) == aRate);
+ MOZ_ASSERT(aChannels == 1 || aChannels == 2);
+ MOZ_ASSERT(aAudioData);
+
+ uint32_t audio_10ms = aRate / 100;
+
+ if (!mPacketizer || mPacketizer->mPacketSize != audio_10ms ||
+ mPacketizer->mChannels != aChannels) {
+ // It's the right thing to drop the bit of audio still in the packetizer:
+ // we don't want to send to the conduit audio that has two different
+ // rates while telling it that it has a constante rate.
+ mPacketizer =
+ MakeUnique<AudioPacketizer<int16_t, int16_t>>(audio_10ms, aChannels);
+ mPacket = MakeUnique<int16_t[]>(audio_10ms * aChannels);
+ }
+
+ mPacketizer->Input(aAudioData, aFrameCount);
+
+ while (mPacketizer->PacketsAvailable()) {
+ mPacketizer->Output(mPacket.get());
+ auto frame = std::make_unique<webrtc::AudioFrame>();
+ // UpdateFrame makes a copy of the audio data.
+ frame->UpdateFrame(frame->timestamp_, mPacket.get(),
+ mPacketizer->mPacketSize, aRate, frame->speech_type_,
+ frame->vad_activity_, mPacketizer->mChannels);
+ mConduit->SendAudioFrame(std::move(frame));
+ }
+ }
+
+ void QueueAudioChunk(TrackRate aRate, const AudioChunk& aChunk,
+ bool aEnabled) {
+ RefPtr<AudioProxyThread> self = this;
+ nsresult rv = mTaskQueue->Dispatch(NS_NewRunnableFunction(
+ "AudioProxyThread::QueueAudioChunk", [self, aRate, aChunk, aEnabled]() {
+ self->InternalProcessAudioChunk(aRate, aChunk, aEnabled);
+ }));
+ MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
+ Unused << rv;
+ }
+
+ protected:
+ virtual ~AudioProxyThread() { MOZ_COUNT_DTOR(AudioProxyThread); }
+
+ const RefPtr<AudioSessionConduit> mConduit;
+ const RefPtr<TaskQueue> mTaskQueue;
+ // Only accessed on mTaskQueue
+ UniquePtr<AudioPacketizer<int16_t, int16_t>> mPacketizer;
+ // A buffer to hold a single packet of audio.
+ UniquePtr<int16_t[]> mPacket;
+ nsTArray<int16_t> mInterleavedAudio;
+ AlignedShortBuffer mOutputAudio;
+ UniquePtr<AudioConverter> mAudioConverter;
+};
+
+MediaPipeline::MediaPipeline(const std::string& aPc,
+ RefPtr<MediaTransportHandler> aTransportHandler,
+ DirectionType aDirection,
+ RefPtr<AbstractThread> aCallThread,
+ RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<MediaSessionConduit> aConduit)
+ : mConduit(std::move(aConduit)),
+ mDirection(aDirection),
+ mCallThread(std::move(aCallThread)),
+ mStsThread(aStsThread),
+ mActive(false, "MediaPipeline::mActive"),
+ mLevel(0),
+ mTransportHandler(std::move(aTransportHandler)),
+ mRtpPacketsSent(0),
+ mRtcpPacketsSent(0),
+ mRtpPacketsReceived(0),
+ mRtcpPacketsReceived(0),
+ mRtpBytesSent(0),
+ mRtpBytesReceived(0),
+ mPc(aPc),
+ mFilter(),
+ mRtpHeaderExtensionMap(new webrtc::RtpHeaderExtensionMap()),
+ mPacketDumper(PacketDumper::GetPacketDumper(mPc)) {}
+
+MediaPipeline::~MediaPipeline() {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("Destroying MediaPipeline: %s", mDescription.c_str()));
+}
+
+void MediaPipeline::Start() {
+ MOZ_ASSERT(NS_IsMainThread());
+ mActive = true;
+}
+
+void MediaPipeline::Stop() {
+ MOZ_ASSERT(NS_IsMainThread());
+ mActive = false;
+}
+
+void MediaPipeline::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ Stop();
+
+ RUN_ON_THREAD(mStsThread,
+ WrapRunnable(RefPtr<MediaPipeline>(this),
+ &MediaPipeline::DetachTransport_s),
+ NS_DISPATCH_NORMAL);
+}
+
+void MediaPipeline::DetachTransport_s() {
+ ASSERT_ON_THREAD(mStsThread);
+
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("%s in %s", mDescription.c_str(), __FUNCTION__));
+
+ disconnect_all();
+ mRtpState = TransportLayer::TS_NONE;
+ mRtcpState = TransportLayer::TS_NONE;
+ mTransportId.clear();
+ mConduit->SetTransportActive(false);
+ mRtpSendEventListener.DisconnectIfExists();
+ mSenderRtcpSendEventListener.DisconnectIfExists();
+ mReceiverRtcpSendEventListener.DisconnectIfExists();
+}
+
+void MediaPipeline::UpdateTransport_m(
+ const std::string& aTransportId, UniquePtr<MediaPipelineFilter>&& aFilter) {
+ mStsThread->Dispatch(NS_NewRunnableFunction(
+ __func__, [aTransportId, filter = std::move(aFilter),
+ self = RefPtr<MediaPipeline>(this)]() mutable {
+ self->UpdateTransport_s(aTransportId, std::move(filter));
+ }));
+}
+
+void MediaPipeline::UpdateTransport_s(
+ const std::string& aTransportId, UniquePtr<MediaPipelineFilter>&& aFilter) {
+ ASSERT_ON_THREAD(mStsThread);
+ if (!mSignalsConnected) {
+ mTransportHandler->SignalStateChange.connect(
+ this, &MediaPipeline::RtpStateChange);
+ mTransportHandler->SignalRtcpStateChange.connect(
+ this, &MediaPipeline::RtcpStateChange);
+ mTransportHandler->SignalEncryptedSending.connect(
+ this, &MediaPipeline::EncryptedPacketSending);
+ mTransportHandler->SignalPacketReceived.connect(
+ this, &MediaPipeline::PacketReceived);
+ mTransportHandler->SignalAlpnNegotiated.connect(
+ this, &MediaPipeline::AlpnNegotiated);
+ mSignalsConnected = true;
+ }
+
+ if (aTransportId != mTransportId) {
+ mTransportId = aTransportId;
+ mRtpState = mTransportHandler->GetState(mTransportId, false);
+ mRtcpState = mTransportHandler->GetState(mTransportId, true);
+ CheckTransportStates();
+ }
+
+ if (mFilter) {
+ for (const auto& extension : mFilter->GetExtmap()) {
+ mRtpHeaderExtensionMap->Deregister(extension.uri);
+ }
+ }
+ if (mFilter && aFilter) {
+ // Use the new filter, but don't forget any remote SSRCs that we've learned
+ // by receiving traffic.
+ mFilter->Update(*aFilter);
+ } else {
+ mFilter = std::move(aFilter);
+ }
+ if (mFilter) {
+ for (const auto& extension : mFilter->GetExtmap()) {
+ mRtpHeaderExtensionMap->RegisterByUri(extension.id, extension.uri);
+ }
+ }
+}
+
+void MediaPipeline::GetContributingSourceStats(
+ const nsString& aInboundRtpStreamId,
+ FallibleTArray<dom::RTCRTPContributingSourceStats>& aArr) const {
+ ASSERT_ON_THREAD(mStsThread);
+ // Get the expiry from now
+ DOMHighResTimeStamp expiry =
+ RtpCSRCStats::GetExpiryFromTime(GetTimestampMaker().GetNow());
+ for (auto info : mCsrcStats) {
+ if (!info.second.Expired(expiry)) {
+ RTCRTPContributingSourceStats stats;
+ info.second.GetWebidlInstance(stats, aInboundRtpStreamId);
+ if (!aArr.AppendElement(stats, fallible)) {
+ mozalloc_handle_oom(0);
+ }
+ }
+ }
+}
+
+void MediaPipeline::RtpStateChange(const std::string& aTransportId,
+ TransportLayer::State aState) {
+ if (mTransportId != aTransportId) {
+ return;
+ }
+ mRtpState = aState;
+ CheckTransportStates();
+}
+
+void MediaPipeline::RtcpStateChange(const std::string& aTransportId,
+ TransportLayer::State aState) {
+ if (mTransportId != aTransportId) {
+ return;
+ }
+ mRtcpState = aState;
+ CheckTransportStates();
+}
+
+void MediaPipeline::CheckTransportStates() {
+ ASSERT_ON_THREAD(mStsThread);
+
+ if (mRtpState == TransportLayer::TS_CLOSED ||
+ mRtpState == TransportLayer::TS_ERROR ||
+ mRtcpState == TransportLayer::TS_CLOSED ||
+ mRtcpState == TransportLayer::TS_ERROR) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Warning,
+ ("RTP Transport failed for pipeline %p flow %s", this,
+ mDescription.c_str()));
+
+ NS_WARNING(
+ "MediaPipeline Transport failed. This is not properly cleaned up yet");
+ // TODO(ekr@rtfm.com): SECURITY: Figure out how to clean up if the
+ // connection was good and now it is bad.
+ // TODO(ekr@rtfm.com): Report up so that the PC knows we
+ // have experienced an error.
+ mConduit->SetTransportActive(false);
+ mRtpSendEventListener.DisconnectIfExists();
+ mSenderRtcpSendEventListener.DisconnectIfExists();
+ mReceiverRtcpSendEventListener.DisconnectIfExists();
+ return;
+ }
+
+ if (mRtpState == TransportLayer::TS_OPEN) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("RTP Transport ready for pipeline %p flow %s", this,
+ mDescription.c_str()));
+ }
+
+ if (mRtcpState == TransportLayer::TS_OPEN) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("RTCP Transport ready for pipeline %p flow %s", this,
+ mDescription.c_str()));
+ }
+
+ if (mRtpState == TransportLayer::TS_OPEN && mRtcpState == mRtpState) {
+ if (mDirection == DirectionType::TRANSMIT) {
+ mConduit->ConnectSenderRtcpEvent(mSenderRtcpReceiveEvent);
+ mRtpSendEventListener = mConduit->SenderRtpSendEvent().Connect(
+ mStsThread, this, &MediaPipeline::SendPacket);
+ mSenderRtcpSendEventListener = mConduit->SenderRtcpSendEvent().Connect(
+ mStsThread, this, &MediaPipeline::SendPacket);
+ } else {
+ mConduit->ConnectReceiverRtcpEvent(mReceiverRtcpReceiveEvent);
+ mConduit->ConnectReceiverRtpEvent(mRtpReceiveEvent);
+ mReceiverRtcpSendEventListener =
+ mConduit->ReceiverRtcpSendEvent().Connect(mStsThread, this,
+ &MediaPipeline::SendPacket);
+ }
+ mConduit->SetTransportActive(true);
+ TransportReady_s();
+ }
+}
+
+void MediaPipeline::SendPacket(MediaPacket&& aPacket) {
+ ASSERT_ON_THREAD(mStsThread);
+
+ const bool isRtp = aPacket.type() == MediaPacket::RTP;
+
+ if (isRtp && mRtpState != TransportLayer::TS_OPEN) {
+ return;
+ }
+
+ if (!isRtp && mRtcpState != TransportLayer::TS_OPEN) {
+ return;
+ }
+
+ aPacket.sdp_level() = Some(Level());
+
+ if (RtpLogger::IsPacketLoggingOn()) {
+ RtpLogger::LogPacket(aPacket, false, mDescription);
+ }
+
+ if (isRtp) {
+ mPacketDumper->Dump(Level(), dom::mozPacketDumpType::Rtp, true,
+ aPacket.data(), aPacket.len());
+ IncrementRtpPacketsSent(aPacket);
+ } else {
+ mPacketDumper->Dump(Level(), dom::mozPacketDumpType::Rtcp, true,
+ aPacket.data(), aPacket.len());
+ IncrementRtcpPacketsSent();
+ }
+
+ MOZ_LOG(
+ gMediaPipelineLog, LogLevel::Debug,
+ ("%s sending %s packet", mDescription.c_str(), (isRtp ? "RTP" : "RTCP")));
+
+ mTransportHandler->SendPacket(mTransportId, std::move(aPacket));
+}
+
+void MediaPipeline::IncrementRtpPacketsSent(const MediaPacket& aPacket) {
+ ASSERT_ON_THREAD(mStsThread);
+ ++mRtpPacketsSent;
+ mRtpBytesSent += aPacket.len();
+
+ if (!(mRtpPacketsSent % 100)) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("RTP sent packet count for %s Pipeline %p: %u (%" PRId64 " bytes)",
+ mDescription.c_str(), this, mRtpPacketsSent, mRtpBytesSent));
+ }
+}
+
+void MediaPipeline::IncrementRtcpPacketsSent() {
+ ASSERT_ON_THREAD(mStsThread);
+ ++mRtcpPacketsSent;
+ if (!(mRtcpPacketsSent % 100)) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("RTCP sent packet count for %s Pipeline %p: %u",
+ mDescription.c_str(), this, mRtcpPacketsSent));
+ }
+}
+
+void MediaPipeline::IncrementRtpPacketsReceived(int32_t aBytes) {
+ ASSERT_ON_THREAD(mStsThread);
+ ++mRtpPacketsReceived;
+ mRtpBytesReceived += aBytes;
+ if (!(mRtpPacketsReceived % 100)) {
+ MOZ_LOG(
+ gMediaPipelineLog, LogLevel::Info,
+ ("RTP received packet count for %s Pipeline %p: %u (%" PRId64 " bytes)",
+ mDescription.c_str(), this, mRtpPacketsReceived, mRtpBytesReceived));
+ }
+}
+
+void MediaPipeline::IncrementRtcpPacketsReceived() {
+ ASSERT_ON_THREAD(mStsThread);
+ ++mRtcpPacketsReceived;
+ if (!(mRtcpPacketsReceived % 100)) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("RTCP received packet count for %s Pipeline %p: %u",
+ mDescription.c_str(), this, mRtcpPacketsReceived));
+ }
+}
+
+void MediaPipeline::RtpPacketReceived(const MediaPacket& packet) {
+ ASSERT_ON_THREAD(mStsThread);
+
+ if (mDirection == DirectionType::TRANSMIT) {
+ return;
+ }
+
+ if (!packet.len()) {
+ return;
+ }
+
+ webrtc::RTPHeader header;
+ rtc::CopyOnWriteBuffer packet_buffer(packet.data(), packet.len());
+ webrtc::RtpPacketReceived pktHeader(mRtpHeaderExtensionMap.get());
+ if (!pktHeader.Parse(packet_buffer)) {
+ return;
+ }
+ pktHeader.GetHeader(&header);
+
+ if (mFilter && !mFilter->Filter(header)) {
+ return;
+ }
+
+ // Make sure to only get the time once, and only if we need it by
+ // using getTimestamp() for access
+ DOMHighResTimeStamp now = 0.0;
+ bool hasTime = false;
+
+ // Remove expired RtpCSRCStats
+ if (!mCsrcStats.empty()) {
+ if (!hasTime) {
+ now = GetTimestampMaker().GetNow();
+ hasTime = true;
+ }
+ auto expiry = RtpCSRCStats::GetExpiryFromTime(now);
+ for (auto p = mCsrcStats.begin(); p != mCsrcStats.end();) {
+ if (p->second.Expired(expiry)) {
+ p = mCsrcStats.erase(p);
+ continue;
+ }
+ p++;
+ }
+ }
+
+ // Add new RtpCSRCStats
+ if (header.numCSRCs) {
+ for (auto i = 0; i < header.numCSRCs; i++) {
+ if (!hasTime) {
+ now = GetTimestampMaker().GetNow();
+ hasTime = true;
+ }
+ auto csrcInfo = mCsrcStats.find(header.arrOfCSRCs[i]);
+ if (csrcInfo == mCsrcStats.end()) {
+ mCsrcStats.insert(std::make_pair(
+ header.arrOfCSRCs[i], RtpCSRCStats(header.arrOfCSRCs[i], now)));
+ } else {
+ csrcInfo->second.SetTimestamp(now);
+ }
+ }
+ }
+
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("%s received RTP packet.", mDescription.c_str()));
+ IncrementRtpPacketsReceived(packet.len());
+ OnRtpPacketReceived();
+
+ RtpLogger::LogPacket(packet, true, mDescription);
+
+ // Might be nice to pass ownership of the buffer in this case, but it is a
+ // small optimization in a rare case.
+ mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtp, false,
+ packet.encrypted_data(), packet.encrypted_len());
+
+ mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtp, false, packet.data(),
+ packet.len());
+
+ mRtpReceiveEvent.Notify(packet.Clone(), header);
+}
+
+void MediaPipeline::RtcpPacketReceived(const MediaPacket& packet) {
+ ASSERT_ON_THREAD(mStsThread);
+
+ if (!packet.len()) {
+ return;
+ }
+
+ // We do not filter RTCP. This is because a compound RTCP packet can contain
+ // any collection of RTCP packets, and webrtc.org already knows how to filter
+ // out what it is interested in, and what it is not. Maybe someday we should
+ // have a TransportLayer that breaks up compound RTCP so we can filter them
+ // individually, but I doubt that will matter much.
+
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("%s received RTCP packet.", mDescription.c_str()));
+ IncrementRtcpPacketsReceived();
+
+ RtpLogger::LogPacket(packet, true, mDescription);
+
+ // Might be nice to pass ownership of the buffer in this case, but it is a
+ // small optimization in a rare case.
+ mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Srtcp, false,
+ packet.encrypted_data(), packet.encrypted_len());
+
+ mPacketDumper->Dump(mLevel, dom::mozPacketDumpType::Rtcp, false,
+ packet.data(), packet.len());
+
+ if (StaticPrefs::media_webrtc_net_force_disable_rtcp_reception()) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("%s RTCP packet forced to be dropped", mDescription.c_str()));
+ return;
+ }
+
+ if (mDirection == DirectionType::TRANSMIT) {
+ mSenderRtcpReceiveEvent.Notify(packet.Clone());
+ } else {
+ mReceiverRtcpReceiveEvent.Notify(packet.Clone());
+ }
+}
+
+void MediaPipeline::PacketReceived(const std::string& aTransportId,
+ const MediaPacket& packet) {
+ ASSERT_ON_THREAD(mStsThread);
+
+ if (mTransportId != aTransportId) {
+ return;
+ }
+
+ MOZ_ASSERT(mRtpState == TransportLayer::TS_OPEN);
+ MOZ_ASSERT(mRtcpState == mRtpState);
+
+ switch (packet.type()) {
+ case MediaPacket::RTP:
+ RtpPacketReceived(packet);
+ break;
+ case MediaPacket::RTCP:
+ RtcpPacketReceived(packet);
+ break;
+ default:;
+ }
+}
+
+void MediaPipeline::AlpnNegotiated(const std::string& aAlpn,
+ bool aPrivacyRequested) {
+ ASSERT_ON_THREAD(mStsThread);
+
+ if (aPrivacyRequested) {
+ MakePrincipalPrivate_s();
+ }
+}
+
+void MediaPipeline::EncryptedPacketSending(const std::string& aTransportId,
+ const MediaPacket& aPacket) {
+ ASSERT_ON_THREAD(mStsThread);
+
+ if (mTransportId == aTransportId) {
+ dom::mozPacketDumpType type;
+ if (aPacket.type() == MediaPacket::SRTP) {
+ type = dom::mozPacketDumpType::Srtp;
+ } else if (aPacket.type() == MediaPacket::SRTCP) {
+ type = dom::mozPacketDumpType::Srtcp;
+ } else if (aPacket.type() == MediaPacket::DTLS) {
+ // TODO(bug 1497936): Implement packet dump for DTLS
+ return;
+ } else {
+ MOZ_ASSERT(false);
+ return;
+ }
+ mPacketDumper->Dump(Level(), type, true, aPacket.data(), aPacket.len());
+ }
+}
+
+class MediaPipelineTransmit::PipelineListener
+ : public DirectMediaTrackListener {
+ friend class MediaPipelineTransmit;
+
+ public:
+ explicit PipelineListener(RefPtr<MediaSessionConduit> aConduit)
+ : mConduit(std::move(aConduit)),
+ mActive(false),
+ mEnabled(false),
+ mDirectConnect(false) {}
+
+ ~PipelineListener() {
+ if (mConverter) {
+ mConverter->Shutdown();
+ }
+ }
+
+ void SetActive(bool aActive) {
+ mActive = aActive;
+ if (mConverter) {
+ mConverter->SetActive(aActive);
+ }
+ }
+ void SetEnabled(bool aEnabled) { mEnabled = aEnabled; }
+
+ // These are needed since nested classes don't have access to any particular
+ // instance of the parent
+ void SetAudioProxy(RefPtr<AudioProxyThread> aProxy) {
+ mAudioProcessing = std::move(aProxy);
+ }
+
+ void SetVideoFrameConverter(RefPtr<VideoFrameConverter> aConverter) {
+ mConverter = std::move(aConverter);
+ }
+
+ void OnVideoFrameConverted(webrtc::VideoFrame aVideoFrame) {
+ MOZ_RELEASE_ASSERT(mConduit->type() == MediaSessionConduit::VIDEO);
+ static_cast<VideoSessionConduit*>(mConduit.get())
+ ->SendVideoFrame(std::move(aVideoFrame));
+ }
+
+ // Implement MediaTrackListener
+ void NotifyQueuedChanges(MediaTrackGraph* aGraph, TrackTime aOffset,
+ const MediaSegment& aQueuedMedia) override;
+ void NotifyEnabledStateChanged(MediaTrackGraph* aGraph,
+ bool aEnabled) override;
+
+ // Implement DirectMediaTrackListener
+ void NotifyRealtimeTrackData(MediaTrackGraph* aGraph, TrackTime aOffset,
+ const MediaSegment& aMedia) override;
+ void NotifyDirectListenerInstalled(InstallationResult aResult) override;
+ void NotifyDirectListenerUninstalled() override;
+
+ private:
+ void NewData(const MediaSegment& aMedia, TrackRate aRate = 0);
+
+ const RefPtr<MediaSessionConduit> mConduit;
+ RefPtr<AudioProxyThread> mAudioProcessing;
+ RefPtr<VideoFrameConverter> mConverter;
+
+ // active is true if there is a transport to send on
+ mozilla::Atomic<bool> mActive;
+ // enabled is true if the media access control permits sending
+ // actual content; when false you get black/silence
+ mozilla::Atomic<bool> mEnabled;
+
+ // Written and read on the MediaTrackGraph thread
+ bool mDirectConnect;
+};
+
+MediaPipelineTransmit::MediaPipelineTransmit(
+ const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread,
+ bool aIsVideo, RefPtr<MediaSessionConduit> aConduit)
+ : MediaPipeline(aPc, std::move(aTransportHandler), DirectionType::TRANSMIT,
+ std::move(aCallThread), std::move(aStsThread),
+ std::move(aConduit)),
+ mWatchManager(this, AbstractThread::MainThread()),
+ mIsVideo(aIsVideo),
+ mListener(new PipelineListener(mConduit)),
+ mDomTrack(nullptr, "MediaPipelineTransmit::mDomTrack"),
+ mSendTrackOverride(nullptr, "MediaPipelineTransmit::mSendTrackOverride") {
+ if (!IsVideo()) {
+ mAudioProcessing =
+ MakeAndAddRef<AudioProxyThread>(*mConduit->AsAudioSessionConduit());
+ mListener->SetAudioProxy(mAudioProcessing);
+ } else { // Video
+ mConverter = MakeAndAddRef<VideoFrameConverter>(GetTimestampMaker());
+ mFrameListener = mConverter->VideoFrameConvertedEvent().Connect(
+ mConverter->mTaskQueue,
+ [listener = mListener](webrtc::VideoFrame aFrame) {
+ listener->OnVideoFrameConverted(std::move(aFrame));
+ });
+ mListener->SetVideoFrameConverter(mConverter);
+ }
+
+ mWatchManager.Watch(mActive, &MediaPipelineTransmit::UpdateSendState);
+ mWatchManager.Watch(mDomTrack, &MediaPipelineTransmit::UpdateSendState);
+ mWatchManager.Watch(mSendTrackOverride,
+ &MediaPipelineTransmit::UpdateSendState);
+
+ mDescription = GenerateDescription();
+}
+
+MediaPipelineTransmit::~MediaPipelineTransmit() {
+ mFrameListener.DisconnectIfExists();
+
+ MOZ_ASSERT(!mTransmitting);
+ MOZ_ASSERT(!mDomTrack.Ref());
+}
+
+void MediaPipelineTransmit::Shutdown() {
+ MediaPipeline::Shutdown();
+ MOZ_ASSERT(!mActive);
+ mWatchManager.Shutdown();
+ if (mDomTrack.Ref()) {
+ mDomTrack.Ref()->RemovePrincipalChangeObserver(this);
+ mDomTrack = nullptr;
+ }
+ mUnsettingSendTrack = false;
+ UpdateSendState();
+ MOZ_ASSERT(!mTransmitting);
+}
+
+void MediaPipeline::SetDescription_s(const std::string& description) {
+ ASSERT_ON_THREAD(mStsThread);
+ mDescription = description;
+}
+
+std::string MediaPipelineTransmit::GenerateDescription() const {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ std::stringstream description;
+ description << mPc << "| ";
+ description << (mIsVideo ? "Transmit video[" : "Transmit audio[");
+
+ if (mDomTrack.Ref()) {
+ nsString nsTrackId;
+ mDomTrack.Ref()->GetId(nsTrackId);
+ description << NS_ConvertUTF16toUTF8(nsTrackId).get();
+ } else if (mSendTrackOverride.Ref()) {
+ description << "override " << mSendTrackOverride.Ref().get();
+ } else {
+ description << "no track";
+ }
+
+ description << "]";
+
+ return description.str();
+}
+
+void MediaPipelineTransmit::UpdateSendState() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // This runs because either mActive, mDomTrack or mSendTrackOverride changed,
+ // or because mSendTrack was unset async. Based on these inputs this method
+ // is responsible for hooking up mSendTrack to mListener in order to feed data
+ // to the conduit.
+ //
+ // If we are inactive, or if the send track does not match what we want to
+ // send (mDomTrack or mSendTrackOverride), we must stop feeding data to the
+ // conduit. NB that removing the listener from mSendTrack is async, and we
+ // must wait for it to resolve before adding mListener to another track.
+ // mUnsettingSendTrack gates us until the listener has been removed from
+ // mSendTrack.
+ //
+ // If we are active and the send track does match what we want to send, we
+ // make sure mListener is added to the send track. Either now, or if we're
+ // still waiting for another send track to be removed, during a future call to
+ // this method.
+
+ if (mUnsettingSendTrack) {
+ // We must wait for the send track to be unset before we can set it again,
+ // to avoid races. Once unset this function is triggered again.
+ return;
+ }
+
+ const bool wasTransmitting = mTransmitting;
+
+ const bool haveLiveSendTrack = mSendTrack && !mSendTrack->IsDestroyed();
+ const bool haveLiveDomTrack = mDomTrack.Ref() && !mDomTrack.Ref()->Ended();
+ const bool haveLiveOverrideTrack =
+ mSendTrackOverride.Ref() && !mSendTrackOverride.Ref()->IsDestroyed();
+ const bool mustRemoveSendTrack =
+ haveLiveSendTrack && !mSendTrackOverride.Ref() &&
+ (!haveLiveDomTrack || mDomTrack.Ref()->GetTrack() != mSendPortSource);
+
+ mTransmitting = mActive && (haveLiveDomTrack || haveLiveOverrideTrack) &&
+ !mustRemoveSendTrack;
+
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("MediaPipeline %p UpdateSendState wasTransmitting=%d, active=%d, "
+ "sendTrack=%p (%s), domTrack=%p (%s), "
+ "sendTrackOverride=%p (%s), mustRemove=%d, mTransmitting=%d",
+ this, wasTransmitting, mActive.Ref(), mSendTrack.get(),
+ haveLiveSendTrack ? "live" : "ended", mDomTrack.Ref().get(),
+ haveLiveDomTrack ? "live" : "ended", mSendTrackOverride.Ref().get(),
+ haveLiveOverrideTrack ? "live" : "ended", mustRemoveSendTrack,
+ mTransmitting));
+
+ if (!wasTransmitting && mTransmitting) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("Attaching pipeline %p to track %p conduit type=%s", this,
+ mDomTrack.Ref().get(), mIsVideo ? "video" : "audio"));
+ if (mDescriptionInvalidated) {
+ // Only update the description when we attach to a track, as detaching is
+ // always a longer async step than updating the description. Updating on
+ // detach would cause the wrong track id to be attributed in logs.
+ RUN_ON_THREAD(mStsThread,
+ WrapRunnable(RefPtr<MediaPipeline>(this),
+ &MediaPipelineTransmit::SetDescription_s,
+ GenerateDescription()),
+ NS_DISPATCH_NORMAL);
+ mDescriptionInvalidated = false;
+ }
+ if (mSendTrackOverride.Ref()) {
+ // Special path that allows unittests to avoid mDomTrack and the graph by
+ // manually calling SetSendTrack.
+ mSendTrack = mSendTrackOverride.Ref();
+ } else {
+ mSendTrack = mDomTrack.Ref()->Graph()->CreateForwardedInputTrack(
+ mDomTrack.Ref()->GetTrack()->mType);
+ mSendPortSource = mDomTrack.Ref()->GetTrack();
+ mSendPort = mSendTrack->AllocateInputPort(mSendPortSource.get());
+ }
+ if (mIsVideo) {
+ mConverter->SetTrackingId(mDomTrack.Ref()->GetSource().mTrackingId);
+ }
+ mSendTrack->QueueSetAutoend(false);
+ if (mIsVideo) {
+ mSendTrack->AddDirectListener(mListener);
+ }
+ mSendTrack->AddListener(mListener);
+ }
+
+ if (wasTransmitting && !mTransmitting) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("Detaching pipeline %p from track %p conduit type=%s", this,
+ mDomTrack.Ref().get(), mIsVideo ? "video" : "audio"));
+ mUnsettingSendTrack = true;
+ if (mIsVideo) {
+ mSendTrack->RemoveDirectListener(mListener);
+ }
+ mSendTrack->RemoveListener(mListener)->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [this, self = RefPtr<MediaPipelineTransmit>(this)] {
+ mUnsettingSendTrack = false;
+ mSendTrack = nullptr;
+ if (!mWatchManager.IsShutdown()) {
+ mWatchManager.ManualNotify(&MediaPipelineTransmit::UpdateSendState);
+ }
+ });
+ if (!mSendTrackOverride.Ref()) {
+ // If an override is set it may be re-used.
+ mSendTrack->Destroy();
+ mSendPort->Destroy();
+ mSendPort = nullptr;
+ mSendPortSource = nullptr;
+ }
+ }
+}
+
+bool MediaPipelineTransmit::Transmitting() const {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return mActive;
+}
+
+bool MediaPipelineTransmit::IsVideo() const { return mIsVideo; }
+
+void MediaPipelineTransmit::PrincipalChanged(dom::MediaStreamTrack* aTrack) {
+ MOZ_ASSERT(aTrack && aTrack == mDomTrack.Ref());
+
+ PeerConnectionWrapper pcw(mPc);
+ if (pcw.impl()) {
+ Document* doc = pcw.impl()->GetParentObject()->GetExtantDoc();
+ if (doc) {
+ UpdateSinkIdentity(doc->NodePrincipal(), pcw.impl()->GetPeerIdentity());
+ } else {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("Can't update sink principal; document gone"));
+ }
+ }
+}
+
+void MediaPipelineTransmit::UpdateSinkIdentity(
+ nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mDomTrack.Ref()) {
+ // Nothing to do here
+ return;
+ }
+
+ bool enableTrack = aPrincipal->Subsumes(mDomTrack.Ref()->GetPrincipal());
+ if (!enableTrack) {
+ // first try didn't work, but there's a chance that this is still available
+ // if our track is bound to a peerIdentity, and the peer connection (our
+ // sink) is bound to the same identity, then we can enable the track.
+ const PeerIdentity* trackIdentity = mDomTrack.Ref()->GetPeerIdentity();
+ if (aSinkIdentity && trackIdentity) {
+ enableTrack = (*aSinkIdentity == *trackIdentity);
+ }
+ }
+
+ mListener->SetEnabled(enableTrack);
+}
+
+void MediaPipelineTransmit::TransportReady_s() {
+ ASSERT_ON_THREAD(mStsThread);
+ // Call base ready function.
+ MediaPipeline::TransportReady_s();
+ mListener->SetActive(true);
+}
+
+nsresult MediaPipelineTransmit::SetTrack(RefPtr<MediaStreamTrack> aDomTrack) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mDomTrack.Ref()) {
+ mDomTrack.Ref()->RemovePrincipalChangeObserver(this);
+ }
+
+ if (aDomTrack) {
+ nsString nsTrackId;
+ aDomTrack->GetId(nsTrackId);
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("Reattaching pipeline to track %p track %s conduit type: %s",
+ aDomTrack.get(), NS_ConvertUTF16toUTF8(nsTrackId).get(),
+ mIsVideo ? "video" : "audio"));
+ }
+
+ mDescriptionInvalidated = true;
+ mDomTrack = std::move(aDomTrack);
+ if (mDomTrack.Ref()) {
+ mDomTrack.Ref()->AddPrincipalChangeObserver(this);
+ PrincipalChanged(mDomTrack.Ref());
+ }
+
+ return NS_OK;
+}
+
+RefPtr<dom::MediaStreamTrack> MediaPipelineTransmit::GetTrack() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mDomTrack;
+}
+
+void MediaPipelineTransmit::SetSendTrackOverride(
+ RefPtr<ProcessedMediaTrack> aSendTrack) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(!mSendTrack);
+ MOZ_RELEASE_ASSERT(!mSendPort);
+ MOZ_RELEASE_ASSERT(!mSendTrackOverride.Ref());
+ mDescriptionInvalidated = true;
+ mSendTrackOverride = std::move(aSendTrack);
+}
+
+// Called if we're attached with AddDirectListener()
+void MediaPipelineTransmit::PipelineListener::NotifyRealtimeTrackData(
+ MediaTrackGraph* aGraph, TrackTime aOffset, const MediaSegment& aMedia) {
+ MOZ_LOG(
+ gMediaPipelineLog, LogLevel::Debug,
+ ("MediaPipeline::NotifyRealtimeTrackData() listener=%p, offset=%" PRId64
+ ", duration=%" PRId64,
+ this, aOffset, aMedia.GetDuration()));
+ TRACE_COMMENT(
+ "MediaPipelineTransmit::PipelineListener::NotifyRealtimeTrackData", "%s",
+ aMedia.GetType() == MediaSegment::VIDEO ? "Video" : "Audio");
+ NewData(aMedia, aGraph->GraphRate());
+}
+
+void MediaPipelineTransmit::PipelineListener::NotifyQueuedChanges(
+ MediaTrackGraph* aGraph, TrackTime aOffset,
+ const MediaSegment& aQueuedMedia) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("MediaPipeline::NotifyQueuedChanges()"));
+
+ if (aQueuedMedia.GetType() == MediaSegment::VIDEO) {
+ // We always get video from the direct listener.
+ return;
+ }
+
+ TRACE("MediaPipelineTransmit::PipelineListener::NotifyQueuedChanges (Audio)");
+
+ if (mDirectConnect) {
+ // ignore non-direct data if we're also getting direct data
+ return;
+ }
+
+ size_t rate;
+ if (aGraph) {
+ rate = aGraph->GraphRate();
+ } else {
+ // When running tests, graph may be null. In that case use a default.
+ rate = 16000;
+ }
+ NewData(aQueuedMedia, rate);
+}
+
+void MediaPipelineTransmit::PipelineListener::NotifyEnabledStateChanged(
+ MediaTrackGraph* aGraph, bool aEnabled) {
+ if (mConduit->type() != MediaSessionConduit::VIDEO) {
+ return;
+ }
+ MOZ_ASSERT(mConverter);
+ mConverter->SetTrackEnabled(aEnabled);
+}
+
+void MediaPipelineTransmit::PipelineListener::NotifyDirectListenerInstalled(
+ InstallationResult aResult) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Info,
+ ("MediaPipeline::NotifyDirectListenerInstalled() listener=%p,"
+ " result=%d",
+ this, static_cast<int32_t>(aResult)));
+
+ mDirectConnect = InstallationResult::SUCCESS == aResult;
+}
+
+void MediaPipelineTransmit::PipelineListener::
+ NotifyDirectListenerUninstalled() {
+ MOZ_LOG(
+ gMediaPipelineLog, LogLevel::Info,
+ ("MediaPipeline::NotifyDirectListenerUninstalled() listener=%p", this));
+
+ if (mConduit->type() == MediaSessionConduit::VIDEO) {
+ // Reset the converter's track-enabled state. If re-added to a new track
+ // later and that track is disabled, we will be signaled explicitly.
+ MOZ_ASSERT(mConverter);
+ mConverter->SetTrackEnabled(true);
+ }
+
+ mDirectConnect = false;
+}
+
+void MediaPipelineTransmit::PipelineListener::NewData(
+ const MediaSegment& aMedia, TrackRate aRate /* = 0 */) {
+ if (mConduit->type() != (aMedia.GetType() == MediaSegment::AUDIO
+ ? MediaSessionConduit::AUDIO
+ : MediaSessionConduit::VIDEO)) {
+ MOZ_ASSERT(false,
+ "The media type should always be correct since the "
+ "listener is locked to a specific track");
+ return;
+ }
+
+ // TODO(ekr@rtfm.com): For now assume that we have only one
+ // track type and it's destined for us
+ // See bug 784517
+ if (aMedia.GetType() == MediaSegment::AUDIO) {
+ MOZ_RELEASE_ASSERT(aRate > 0);
+
+ if (!mActive) {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("Discarding audio packets because transport not ready"));
+ return;
+ }
+
+ const AudioSegment* audio = static_cast<const AudioSegment*>(&aMedia);
+ for (AudioSegment::ConstChunkIterator iter(*audio); !iter.IsEnded();
+ iter.Next()) {
+ mAudioProcessing->QueueAudioChunk(aRate, *iter, mEnabled);
+ }
+ } else {
+ const VideoSegment* video = static_cast<const VideoSegment*>(&aMedia);
+
+ for (VideoSegment::ConstChunkIterator iter(*video); !iter.IsEnded();
+ iter.Next()) {
+ mConverter->QueueVideoChunk(*iter, !mEnabled);
+ }
+ }
+}
+
+class GenericReceiveListener : public MediaTrackListener {
+ public:
+ explicit GenericReceiveListener(const RefPtr<dom::MediaStreamTrack>& aTrack)
+ : mTrackSource(new nsMainThreadPtrHolder<RemoteTrackSource>(
+ "GenericReceiveListener::mTrackSource",
+ &static_cast<RemoteTrackSource&>(aTrack->GetSource()))),
+ mSource(mTrackSource->mStream),
+ mTrackingId(mTrackSource->mTrackingId),
+ mIsAudio(aTrack->AsAudioStreamTrack()),
+ mEnabled(false),
+ mMaybeTrackNeedsUnmute(true) {
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ MOZ_DIAGNOSTIC_ASSERT(mSource, "Must be used with a SourceMediaTrack");
+ }
+
+ virtual ~GenericReceiveListener() = default;
+
+ void SetEnabled(bool aEnabled) {
+ if (mEnabled == aEnabled) {
+ return;
+ }
+ mEnabled = aEnabled;
+ if (aEnabled) {
+ mMaybeTrackNeedsUnmute = true;
+ }
+ if (mIsAudio && !mSource->IsDestroyed()) {
+ mSource->SetPullingEnabled(mEnabled);
+ }
+ }
+
+ void OnRtpReceived() {
+ if (mMaybeTrackNeedsUnmute) {
+ mMaybeTrackNeedsUnmute = false;
+ GetMainThreadEventTarget()->Dispatch(
+ NewRunnableMethod("GenericReceiveListener::OnRtpReceived_m", this,
+ &GenericReceiveListener::OnRtpReceived_m));
+ }
+ }
+
+ void OnRtpReceived_m() {
+ if (mEnabled) {
+ mTrackSource->SetMuted(false);
+ }
+ }
+
+ void EndTrack() {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Debug,
+ ("GenericReceiveListener ending track"));
+
+ if (!mSource->IsDestroyed()) {
+ // This breaks the cycle with the SourceMediaTrack
+ mSource->RemoveListener(this);
+ mSource->End();
+ mSource->Destroy();
+ }
+
+ GetMainThreadEventTarget()->Dispatch(
+ NewRunnableMethod("RemoteTrackSource::ForceEnded", mTrackSource.get(),
+ &RemoteTrackSource::ForceEnded));
+ }
+
+ protected:
+ const nsMainThreadPtrHandle<RemoteTrackSource> mTrackSource;
+ const RefPtr<SourceMediaTrack> mSource;
+ const TrackingId mTrackingId;
+ const bool mIsAudio;
+ // Main thread only.
+ bool mEnabled;
+ // Any thread.
+ Atomic<bool> mMaybeTrackNeedsUnmute;
+};
+
+MediaPipelineReceive::MediaPipelineReceive(
+ const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<MediaSessionConduit> aConduit)
+ : MediaPipeline(aPc, std::move(aTransportHandler), DirectionType::RECEIVE,
+ std::move(aCallThread), std::move(aStsThread),
+ std::move(aConduit)) {}
+
+MediaPipelineReceive::~MediaPipelineReceive() = default;
+
+class MediaPipelineReceiveAudio::PipelineListener
+ : public GenericReceiveListener {
+ public:
+ PipelineListener(const RefPtr<dom::MediaStreamTrack>& aTrack,
+ RefPtr<MediaSessionConduit> aConduit,
+ const PrincipalHandle& aPrincipalHandle)
+ : GenericReceiveListener(aTrack),
+ mConduit(std::move(aConduit)),
+ // AudioSession conduit only supports 16, 32, 44.1 and 48kHz
+ // This is an artificial limitation, it would however require more
+ // changes to support any rates. If the sampling rate is not-supported,
+ // we will use 48kHz instead.
+ mRate(static_cast<AudioSessionConduit*>(mConduit.get())
+ ->IsSamplingFreqSupported(mSource->Graph()->GraphRate())
+ ? mSource->Graph()->GraphRate()
+ : WEBRTC_MAX_SAMPLE_RATE),
+ mTaskQueue(TaskQueue::Create(
+ GetMediaThreadPool(MediaThreadType::WEBRTC_WORKER),
+ "AudioPipelineListener")),
+ mPlayedTicks(0),
+ mAudioFrame(std::make_unique<webrtc::AudioFrame>()),
+ mPrincipalHandle(aPrincipalHandle),
+ mForceSilence(false) {}
+
+ void Init() {
+ mSource->SetAppendDataSourceRate(mRate);
+ mSource->AddListener(this);
+ }
+
+ // Implement MediaTrackListener
+ void NotifyPull(MediaTrackGraph* aGraph, TrackTime aEndOfAppendedData,
+ TrackTime aDesiredTime) override {
+ NotifyPullImpl(aDesiredTime);
+ }
+
+ void MakePrincipalPrivate_s() {
+ mForceSilence = true;
+
+ GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
+ "MediaPipelineReceiveAudio::PipelineListener::MakePrincipalPrivate_s",
+ [self = RefPtr<PipelineListener>(this), this] {
+ class Message : public ControlMessage {
+ public:
+ Message(RefPtr<PipelineListener> aListener,
+ const PrincipalHandle& aPrivatePrincipal)
+ : ControlMessage(nullptr),
+ mListener(std::move(aListener)),
+ mPrivatePrincipal(aPrivatePrincipal) {}
+
+ void Run() override {
+ mListener->mPrincipalHandle = mPrivatePrincipal;
+ mListener->mForceSilence = false;
+ }
+
+ const RefPtr<PipelineListener> mListener;
+ PrincipalHandle mPrivatePrincipal;
+ };
+
+ RefPtr<nsIPrincipal> privatePrincipal =
+ NullPrincipal::CreateWithInheritedAttributes(
+ mTrackSource->GetPrincipal());
+ mTrackSource->SetPrincipal(privatePrincipal);
+
+ if (mSource->IsDestroyed()) {
+ return;
+ }
+
+ mSource->GraphImpl()->AppendMessage(
+ MakeUnique<Message>(this, MakePrincipalHandle(privatePrincipal)));
+ }));
+ }
+
+ private:
+ ~PipelineListener() = default;
+
+ void NotifyPullImpl(TrackTime aDesiredTime) {
+ TRACE_COMMENT("PiplineListener::NotifyPullImpl", "PipelineListener %p",
+ this);
+ uint32_t samplesPer10ms = mRate / 100;
+
+ // mSource's rate is not necessarily the same as the graph rate, since there
+ // are sample-rate constraints on the inbound audio: only 16, 32, 44.1 and
+ // 48kHz are supported. The audio frames we get here is going to be
+ // resampled when inserted into the graph. aDesiredTime and mPlayedTicks are
+ // in the graph rate.
+
+ while (mPlayedTicks < aDesiredTime) {
+ // This fetches 10ms of data, either mono or stereo
+ MediaConduitErrorCode err =
+ static_cast<AudioSessionConduit*>(mConduit.get())
+ ->GetAudioFrame(mRate, mAudioFrame.get());
+
+ if (err != kMediaConduitNoError) {
+ // Insert silence on conduit/GIPS failure (extremely unlikely)
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Error,
+ ("Audio conduit failed (%d) to return data @ %" PRId64
+ " (desired %" PRId64 " -> %f)",
+ err, mPlayedTicks, aDesiredTime,
+ mSource->TrackTimeToSeconds(aDesiredTime)));
+ constexpr size_t mono = 1;
+ mAudioFrame->UpdateFrame(
+ mAudioFrame->timestamp_, nullptr, samplesPer10ms, mRate,
+ mAudioFrame->speech_type_, mAudioFrame->vad_activity_,
+ std::max(mono, mAudioFrame->num_channels()));
+ }
+
+ MOZ_LOG(
+ gMediaPipelineLog, LogLevel::Debug,
+ ("Audio conduit returned buffer for %zu channels, %zu frames",
+ mAudioFrame->num_channels(), mAudioFrame->samples_per_channel()));
+
+ AudioSegment segment;
+ if (mForceSilence || mAudioFrame->muted()) {
+ segment.AppendNullData(mAudioFrame->samples_per_channel());
+ } else {
+ CheckedInt<size_t> bufferSize(sizeof(uint16_t));
+ bufferSize *= mAudioFrame->samples_per_channel();
+ bufferSize *= mAudioFrame->num_channels();
+ RefPtr<SharedBuffer> samples = SharedBuffer::Create(bufferSize);
+ int16_t* samplesData = static_cast<int16_t*>(samples->Data());
+ AutoTArray<int16_t*, 2> channels;
+ AutoTArray<const int16_t*, 2> outputChannels;
+
+ channels.SetLength(mAudioFrame->num_channels());
+
+ size_t offset = 0;
+ for (size_t i = 0; i < mAudioFrame->num_channels(); i++) {
+ channels[i] = samplesData + offset;
+ offset += mAudioFrame->samples_per_channel();
+ }
+
+ DeinterleaveAndConvertBuffer(
+ mAudioFrame->data(), mAudioFrame->samples_per_channel(),
+ mAudioFrame->num_channels(), channels.Elements());
+
+ outputChannels.AppendElements(channels);
+
+ segment.AppendFrames(samples.forget(), outputChannels,
+ mAudioFrame->samples_per_channel(),
+ mPrincipalHandle);
+ }
+
+ // Handle track not actually added yet or removed/finished
+ if (TrackTime appended = mSource->AppendData(&segment)) {
+ mPlayedTicks += appended;
+ } else {
+ MOZ_LOG(gMediaPipelineLog, LogLevel::Error, ("AppendData failed"));
+ // we can't un-read the data, but that's ok since we don't want to
+ // buffer - but don't i-loop!
+ break;
+ }
+ }
+ }
+
+ const RefPtr<MediaSessionConduit> mConduit;
+ // This conduit's sampling rate. This is either 16, 32, 44.1 or 48kHz, and
+ // tries to be the same as the graph rate. If the graph rate is higher than
+ // 48kHz, mRate is capped to 48kHz. If mRate does not match the graph rate,
+ // audio is resampled to the graph rate.
+ const TrackRate mRate;
+ const RefPtr<TaskQueue> mTaskQueue;
+ // Number of frames of data that has been added to the SourceMediaTrack in
+ // the graph's rate. Graph thread only.
+ TrackTicks mPlayedTicks;
+ // Allocation of an audio frame used as a scratch buffer when reading data out
+ // of libwebrtc for forwarding into the graph. Graph thread only.
+ std::unique_ptr<webrtc::AudioFrame> mAudioFrame;
+ // Principal handle used when appending data to the SourceMediaTrack. Graph
+ // thread only.
+ PrincipalHandle mPrincipalHandle;
+ // Set to true on the sts thread if privacy is requested when ALPN was
+ // negotiated. Set to false again when mPrincipalHandle is private.
+ Atomic<bool> mForceSilence;
+};
+
+MediaPipelineReceiveAudio::MediaPipelineReceiveAudio(
+ const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<AudioSessionConduit> aConduit,
+ const RefPtr<dom::MediaStreamTrack>& aTrack,
+ const PrincipalHandle& aPrincipalHandle)
+ : MediaPipelineReceive(aPc, std::move(aTransportHandler),
+ std::move(aCallThread), std::move(aStsThread),
+ std::move(aConduit)),
+ mListener(aTrack
+ ? new PipelineListener(aTrack, mConduit, aPrincipalHandle)
+ : nullptr),
+ mWatchManager(this, AbstractThread::MainThread()) {
+ mDescription = mPc + "| Receive audio";
+ if (mListener) {
+ mListener->Init();
+ }
+ mWatchManager.Watch(mActive, &MediaPipelineReceiveAudio::UpdateListener);
+}
+
+void MediaPipelineReceiveAudio::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MediaPipeline::Shutdown();
+ mWatchManager.Shutdown();
+ if (mListener) {
+ mListener->EndTrack();
+ }
+}
+
+void MediaPipelineReceiveAudio::MakePrincipalPrivate_s() {
+ ASSERT_ON_THREAD(mStsThread);
+ if (mListener) {
+ mListener->MakePrincipalPrivate_s();
+ }
+}
+
+void MediaPipelineReceiveAudio::OnRtpPacketReceived() {
+ ASSERT_ON_THREAD(mStsThread);
+ if (mListener) {
+ mListener->OnRtpReceived();
+ }
+}
+
+void MediaPipelineReceiveAudio::UpdateListener() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mListener) {
+ mListener->SetEnabled(mActive.Ref());
+ }
+}
+
+class MediaPipelineReceiveVideo::PipelineListener
+ : public GenericReceiveListener {
+ public:
+ PipelineListener(const RefPtr<dom::MediaStreamTrack>& aTrack,
+ const PrincipalHandle& aPrincipalHandle)
+ : GenericReceiveListener(aTrack),
+ mImageContainer(
+ MakeAndAddRef<ImageContainer>(ImageContainer::ASYNCHRONOUS)),
+ mMutex("MediaPipelineReceiveVideo::PipelineListener::mMutex"),
+ mPrincipalHandle(aPrincipalHandle) {}
+
+ void Init() { mSource->AddListener(this); }
+
+ void MakePrincipalPrivate_s() {
+ {
+ MutexAutoLock lock(mMutex);
+ mForceDropFrames = true;
+ }
+
+ GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
+ __func__, [self = RefPtr<PipelineListener>(this), this] {
+ RefPtr<nsIPrincipal> privatePrincipal =
+ NullPrincipal::CreateWithInheritedAttributes(
+ mTrackSource->GetPrincipal());
+ mTrackSource->SetPrincipal(privatePrincipal);
+
+ MutexAutoLock lock(mMutex);
+ mPrincipalHandle = MakePrincipalHandle(privatePrincipal);
+ mForceDropFrames = false;
+ }));
+ }
+
+ void RenderVideoFrame(const webrtc::VideoFrameBuffer& aBuffer,
+ uint32_t aTimeStamp, int64_t aRenderTime) {
+ PrincipalHandle principal;
+ {
+ MutexAutoLock lock(mMutex);
+ if (mForceDropFrames) {
+ return;
+ }
+ principal = mPrincipalHandle;
+ }
+ RefPtr<Image> image;
+ if (aBuffer.type() == webrtc::VideoFrameBuffer::Type::kNative) {
+ // We assume that only native handles are used with the
+ // WebrtcMediaDataCodec decoder.
+ const ImageBuffer* imageBuffer =
+ static_cast<const ImageBuffer*>(&aBuffer);
+ image = imageBuffer->GetNativeImage();
+ } else {
+ MOZ_ASSERT(aBuffer.type() == webrtc::VideoFrameBuffer::Type::kI420);
+ rtc::scoped_refptr<const webrtc::I420BufferInterface> i420(
+ aBuffer.GetI420());
+
+ MOZ_ASSERT(i420->DataY());
+ // Create a video frame using |buffer|.
+ PerformanceRecorder<CopyVideoStage> rec(
+ "MediaPipelineReceiveVideo::CopyToImage"_ns, mTrackingId,
+ i420->width(), i420->height());
+
+ RefPtr<PlanarYCbCrImage> yuvImage =
+ mImageContainer->CreatePlanarYCbCrImage();
+
+ PlanarYCbCrData yuvData;
+ yuvData.mYChannel = const_cast<uint8_t*>(i420->DataY());
+ yuvData.mYStride = i420->StrideY();
+ MOZ_ASSERT(i420->StrideU() == i420->StrideV());
+ yuvData.mCbCrStride = i420->StrideU();
+ yuvData.mCbChannel = const_cast<uint8_t*>(i420->DataU());
+ yuvData.mCrChannel = const_cast<uint8_t*>(i420->DataV());
+ yuvData.mPictureRect = IntRect(0, 0, i420->width(), i420->height());
+ yuvData.mStereoMode = StereoMode::MONO;
+ // This isn't the best default.
+ yuvData.mYUVColorSpace = gfx::YUVColorSpace::BT601;
+ yuvData.mChromaSubsampling =
+ gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
+
+ if (!yuvImage->CopyData(yuvData)) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ rec.Record();
+
+ image = std::move(yuvImage);
+ }
+
+ VideoSegment segment;
+ auto size = image->GetSize();
+ segment.AppendFrame(image.forget(), size, principal);
+ mSource->AppendData(&segment);
+ }
+
+ private:
+ RefPtr<layers::ImageContainer> mImageContainer;
+ Mutex mMutex MOZ_UNANNOTATED; // Protects the below members.
+ PrincipalHandle mPrincipalHandle;
+ // Set to true on the sts thread if privacy is requested when ALPN was
+ // negotiated. Set to false again when mPrincipalHandle is private.
+ bool mForceDropFrames = false;
+};
+
+class MediaPipelineReceiveVideo::PipelineRenderer
+ : public mozilla::VideoRenderer {
+ public:
+ explicit PipelineRenderer(MediaPipelineReceiveVideo* aPipeline)
+ : mPipeline(aPipeline) {}
+
+ void Detach() { mPipeline = nullptr; }
+
+ // Implement VideoRenderer
+ void FrameSizeChange(unsigned int aWidth, unsigned int aHeight) override {}
+ void RenderVideoFrame(const webrtc::VideoFrameBuffer& aBuffer,
+ uint32_t aTimeStamp, int64_t aRenderTime) override {
+ mPipeline->mListener->RenderVideoFrame(aBuffer, aTimeStamp, aRenderTime);
+ }
+
+ private:
+ MediaPipelineReceiveVideo* mPipeline; // Raw pointer to avoid cycles
+};
+
+MediaPipelineReceiveVideo::MediaPipelineReceiveVideo(
+ const std::string& aPc, RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread, RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<VideoSessionConduit> aConduit,
+ const RefPtr<dom::MediaStreamTrack>& aTrack,
+ const PrincipalHandle& aPrincipalHandle)
+ : MediaPipelineReceive(aPc, std::move(aTransportHandler),
+ std::move(aCallThread), std::move(aStsThread),
+ std::move(aConduit)),
+ mRenderer(new PipelineRenderer(this)),
+ mListener(aTrack ? new PipelineListener(aTrack, aPrincipalHandle)
+ : nullptr),
+ mWatchManager(this, AbstractThread::MainThread()) {
+ mDescription = mPc + "| Receive video";
+ if (mListener) {
+ mListener->Init();
+ }
+ static_cast<VideoSessionConduit*>(mConduit.get())->AttachRenderer(mRenderer);
+ mWatchManager.Watch(mActive, &MediaPipelineReceiveVideo::UpdateListener);
+}
+
+void MediaPipelineReceiveVideo::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MediaPipeline::Shutdown();
+ mWatchManager.Shutdown();
+
+ // stop generating video and thus stop invoking the PipelineRenderer
+ // and PipelineListener - the renderer has a raw ptr to the Pipeline to
+ // avoid cycles, and the render callbacks are invoked from a different
+ // thread so simple null-checks would cause TSAN bugs without locks.
+ static_cast<VideoSessionConduit*>(mConduit.get())->DetachRenderer();
+ if (mListener) {
+ mListener->EndTrack();
+ }
+}
+
+void MediaPipelineReceiveVideo::MakePrincipalPrivate_s() {
+ ASSERT_ON_THREAD(mStsThread);
+ if (mListener) {
+ mListener->MakePrincipalPrivate_s();
+ }
+}
+
+void MediaPipelineReceiveVideo::OnRtpPacketReceived() {
+ ASSERT_ON_THREAD(mStsThread);
+ if (mListener) {
+ mListener->OnRtpReceived();
+ }
+}
+
+void MediaPipelineReceiveVideo::UpdateListener() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mListener) {
+ mListener->SetEnabled(mActive.Ref());
+ }
+}
+
+const dom::RTCStatsTimestampMaker& MediaPipeline::GetTimestampMaker() const {
+ return mConduit->GetTimestampMaker();
+}
+
+DOMHighResTimeStamp MediaPipeline::RtpCSRCStats::GetExpiryFromTime(
+ const DOMHighResTimeStamp aTime) {
+ // DOMHighResTimeStamp is a unit measured in ms
+ return aTime + EXPIRY_TIME_MILLISECONDS;
+}
+
+MediaPipeline::RtpCSRCStats::RtpCSRCStats(const uint32_t aCsrc,
+ const DOMHighResTimeStamp aTime)
+ : mCsrc(aCsrc), mTimestamp(aTime) {}
+
+void MediaPipeline::RtpCSRCStats::GetWebidlInstance(
+ dom::RTCRTPContributingSourceStats& aWebidlObj,
+ const nsString& aInboundRtpStreamId) const {
+ nsString statId = u"csrc_"_ns + aInboundRtpStreamId;
+ statId.AppendLiteral("_");
+ statId.AppendInt(mCsrc);
+ aWebidlObj.mId.Construct(statId);
+ aWebidlObj.mType.Construct(RTCStatsType::Csrc);
+ aWebidlObj.mTimestamp.Construct(mTimestamp);
+ aWebidlObj.mContributorSsrc.Construct(mCsrc);
+ aWebidlObj.mInboundRtpStreamId.Construct(aInboundRtpStreamId);
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transportbridge/MediaPipeline.h b/dom/media/webrtc/transportbridge/MediaPipeline.h
new file mode 100644
index 0000000000..6fe3f5f287
--- /dev/null
+++ b/dom/media/webrtc/transportbridge/MediaPipeline.h
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef mediapipeline_h__
+#define mediapipeline_h__
+
+#include <map>
+
+#include "transport/sigslot.h"
+#include "transport/transportlayer.h" // For TransportLayer::State
+
+#include "libwebrtcglue/MediaConduitInterface.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/StateMirroring.h"
+#include "transport/mediapacket.h"
+#include "transport/runnable_utils.h"
+#include "AudioPacketizer.h"
+#include "MediaEventSource.h"
+#include "MediaPipelineFilter.h"
+#include "MediaSegment.h"
+#include "PrincipalChangeObserver.h"
+#include "jsapi/PacketDumper.h"
+
+#include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
+
+// Should come from MediaEngine.h, but that's a pain to include here
+// because of the MOZILLA_EXTERNAL_LINKAGE stuff.
+#define WEBRTC_MAX_SAMPLE_RATE 48000
+
+class nsIPrincipal;
+
+namespace mozilla {
+class AudioProxyThread;
+class MediaInputPort;
+class MediaPipelineFilter;
+class MediaTransportHandler;
+class PeerIdentity;
+class ProcessedMediaTrack;
+class SourceMediaTrack;
+class VideoFrameConverter;
+
+namespace dom {
+class MediaStreamTrack;
+struct RTCRTPContributingSourceStats;
+} // namespace dom
+
+// A class that represents the pipeline of audio and video
+// The dataflow looks like:
+//
+// TRANSMIT
+// CaptureDevice -> stream -> [us] -> conduit -> [us] -> transport -> network
+//
+// RECEIVE
+// network -> transport -> [us] -> conduit -> [us] -> stream -> Playout
+//
+// The boxes labeled [us] are just bridge logic implemented in this class
+//
+// We have to deal with a number of threads:
+//
+// GSM:
+// * Assembles the pipeline
+// SocketTransportService
+// * Receives notification that ICE and DTLS have completed
+// * Processes incoming network data and passes it to the conduit
+// * Processes outgoing RTP and RTCP
+// MediaTrackGraph
+// * Receives outgoing data from the MediaTrackGraph
+// * Receives pull requests for more data from the
+// MediaTrackGraph
+// One or another GIPS threads
+// * Receives RTCP messages to send to the other side
+// * Processes video frames GIPS wants to render
+//
+// For a transmitting conduit, "output" is RTP and "input" is RTCP.
+// For a receiving conduit, "input" is RTP and "output" is RTCP.
+//
+
+class MediaPipeline : public sigslot::has_slots<> {
+ public:
+ enum class DirectionType { TRANSMIT, RECEIVE };
+ MediaPipeline(const std::string& aPc,
+ RefPtr<MediaTransportHandler> aTransportHandler,
+ DirectionType aDirection, RefPtr<AbstractThread> aCallThread,
+ RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<MediaSessionConduit> aConduit);
+
+ void Start();
+ void Stop();
+
+ void SetLevel(size_t aLevel) { mLevel = aLevel; }
+
+ // Main thread shutdown.
+ virtual void Shutdown();
+
+ void UpdateTransport_m(const std::string& aTransportId,
+ UniquePtr<MediaPipelineFilter>&& aFilter);
+
+ void UpdateTransport_s(const std::string& aTransportId,
+ UniquePtr<MediaPipelineFilter>&& aFilter);
+
+ virtual DirectionType Direction() const { return mDirection; }
+ size_t Level() const { return mLevel; }
+ virtual bool IsVideo() const = 0;
+
+ class RtpCSRCStats {
+ public:
+ // Gets an expiration time for CRC info given a reference time,
+ // this reference time would normally be the time of calling.
+ // This value can then be used to check if a RtpCSRCStats
+ // has expired via Expired(...)
+ static DOMHighResTimeStamp GetExpiryFromTime(
+ const DOMHighResTimeStamp aTime);
+
+ RtpCSRCStats(const uint32_t aCsrc, const DOMHighResTimeStamp aTime);
+ ~RtpCSRCStats() = default;
+ // Initialize a webidl representation suitable for adding to a report.
+ // This assumes that the webidl object is empty.
+ // @param aWebidlObj the webidl binding object to popluate
+ // @param aInboundRtpStreamId the associated RTCInboundRTPStreamStats.id
+ void GetWebidlInstance(dom::RTCRTPContributingSourceStats& aWebidlObj,
+ const nsString& aInboundRtpStreamId) const;
+ void SetTimestamp(const DOMHighResTimeStamp aTime) { mTimestamp = aTime; }
+ // Check if the RtpCSRCStats has expired, checks against a
+ // given expiration time.
+ bool Expired(const DOMHighResTimeStamp aExpiry) const {
+ return mTimestamp < aExpiry;
+ }
+
+ private:
+ static const double constexpr EXPIRY_TIME_MILLISECONDS = 10 * 1000;
+ const uint32_t mCsrc;
+ DOMHighResTimeStamp mTimestamp;
+ };
+
+ // Gets the gathered contributing source stats for the last expiration period.
+ // @param aId the stream id to use for populating inboundRtpStreamId field
+ // @param aArr the array to append the stats objects to
+ void GetContributingSourceStats(
+ const nsString& aInboundRtpStreamId,
+ FallibleTArray<dom::RTCRTPContributingSourceStats>& aArr) const;
+
+ int32_t RtpPacketsSent() const { return mRtpPacketsSent; }
+ int64_t RtpBytesSent() const { return mRtpBytesSent; }
+ int32_t RtcpPacketsSent() const { return mRtcpPacketsSent; }
+ int32_t RtpPacketsReceived() const { return mRtpPacketsReceived; }
+ int64_t RtpBytesReceived() const { return mRtpBytesReceived; }
+ int32_t RtcpPacketsReceived() const { return mRtcpPacketsReceived; }
+
+ const dom::RTCStatsTimestampMaker& GetTimestampMaker() const;
+
+ // Thread counting
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline)
+
+ protected:
+ virtual ~MediaPipeline();
+
+ // The transport is ready
+ virtual void TransportReady_s() {}
+
+ void IncrementRtpPacketsSent(const MediaPacket& aPacket);
+ void IncrementRtcpPacketsSent();
+ void IncrementRtpPacketsReceived(int aBytes);
+ virtual void OnRtpPacketReceived() {}
+ void IncrementRtcpPacketsReceived();
+
+ virtual void SendPacket(MediaPacket&& packet);
+
+ // Process slots on transports
+ void RtpStateChange(const std::string& aTransportId, TransportLayer::State);
+ void RtcpStateChange(const std::string& aTransportId, TransportLayer::State);
+ virtual void CheckTransportStates();
+ void PacketReceived(const std::string& aTransportId,
+ const MediaPacket& packet);
+ void AlpnNegotiated(const std::string& aAlpn, bool aPrivacyRequested);
+
+ void RtpPacketReceived(const MediaPacket& packet);
+ void RtcpPacketReceived(const MediaPacket& packet);
+
+ void EncryptedPacketSending(const std::string& aTransportId,
+ const MediaPacket& aPacket);
+
+ void SetDescription_s(const std::string& description);
+
+ // Called when ALPN is negotiated and is requesting privacy, so receive
+ // pipelines do not enter data into the graph under a content principal.
+ virtual void MakePrincipalPrivate_s() {}
+
+ public:
+ const RefPtr<MediaSessionConduit> mConduit;
+ const DirectionType mDirection;
+
+ // Pointers to the threads we need. Initialized at creation
+ // and used all over the place.
+ const RefPtr<AbstractThread> mCallThread;
+ const RefPtr<nsISerialEventTarget> mStsThread;
+
+ protected:
+ // True if we should be actively transmitting or receiving data. Main thread
+ // only.
+ Watchable<bool> mActive;
+ Atomic<size_t> mLevel;
+ std::string mTransportId;
+ const RefPtr<MediaTransportHandler> mTransportHandler;
+
+ TransportLayer::State mRtpState = TransportLayer::TS_NONE;
+ TransportLayer::State mRtcpState = TransportLayer::TS_NONE;
+ bool mSignalsConnected = false;
+
+ // Only safe to access from STS thread.
+ int32_t mRtpPacketsSent;
+ int32_t mRtcpPacketsSent;
+ int32_t mRtpPacketsReceived;
+ int32_t mRtcpPacketsReceived;
+ int64_t mRtpBytesSent;
+ int64_t mRtpBytesReceived;
+
+ // Only safe to access from STS thread.
+ std::map<uint32_t, RtpCSRCStats> mCsrcStats;
+
+ // Written in c'tor. Read on STS and main thread.
+ const std::string mPc;
+
+ // String describing this MediaPipeline for logging purposes. Only safe to
+ // access from STS thread.
+ std::string mDescription;
+
+ // Written in c'tor, all following accesses are on the STS thread.
+ UniquePtr<MediaPipelineFilter> mFilter;
+ const UniquePtr<webrtc::RtpHeaderExtensionMap> mRtpHeaderExtensionMap;
+
+ RefPtr<PacketDumper> mPacketDumper;
+
+ MediaEventProducerExc<MediaPacket, webrtc::RTPHeader> mRtpReceiveEvent;
+ MediaEventProducerExc<MediaPacket> mSenderRtcpReceiveEvent;
+ MediaEventProducerExc<MediaPacket> mReceiverRtcpReceiveEvent;
+
+ MediaEventListener mRtpSendEventListener;
+ MediaEventListener mSenderRtcpSendEventListener;
+ MediaEventListener mReceiverRtcpSendEventListener;
+
+ private:
+ bool IsRtp(const unsigned char* aData, size_t aLen) const;
+ // Must be called on the STS thread. Must be called after Shutdown().
+ void DetachTransport_s();
+};
+
+// A specialization of pipeline for reading from an input device
+// and transmitting to the network.
+class MediaPipelineTransmit
+ : public MediaPipeline,
+ public dom::PrincipalChangeObserver<dom::MediaStreamTrack> {
+ public:
+ // Set aRtcpTransport to nullptr to use rtcp-mux
+ MediaPipelineTransmit(const std::string& aPc,
+ RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread,
+ RefPtr<nsISerialEventTarget> aStsThread, bool aIsVideo,
+ RefPtr<MediaSessionConduit> aConduit);
+
+ void Shutdown() override;
+
+ bool Transmitting() const;
+
+ // written and used from MainThread
+ bool IsVideo() const override;
+
+ // When the principal of the domtrack changes, it calls through to here
+ // so that we can determine whether to enable track transmission.
+ // In cases where the peer isn't yet identified, we disable the pipeline (not
+ // the stream, that would potentially affect others), so that it sends
+ // black/silence. Once the peer is identified, re-enable those streams.
+ virtual void UpdateSinkIdentity(nsIPrincipal* aPrincipal,
+ const PeerIdentity* aSinkIdentity);
+
+ // for monitoring changes in track ownership
+ void PrincipalChanged(dom::MediaStreamTrack* aTrack) override;
+
+ // Override MediaPipeline::TransportReady_s.
+ void TransportReady_s() override;
+
+ // Replace a track with a different one.
+ nsresult SetTrack(RefPtr<dom::MediaStreamTrack> aDomTrack);
+
+ // Used to correlate stats
+ RefPtr<dom::MediaStreamTrack> GetTrack() const;
+
+ // For test use only. This allows a send track to be set without a
+ // corresponding dom track.
+ void SetSendTrackOverride(RefPtr<ProcessedMediaTrack> aSendTrack);
+
+ // Separate classes to allow ref counting
+ class PipelineListener;
+ class VideoFrameFeeder;
+
+ protected:
+ ~MediaPipelineTransmit();
+
+ // Updates mDescription (async) with information about the track we are
+ // transmitting.
+ std::string GenerateDescription() const;
+
+ // Sets up mSendPort and mSendTrack to feed mConduit if we are transmitting
+ // and have a dom track but no send track. Main thread only.
+ void UpdateSendState();
+
+ private:
+ WatchManager<MediaPipelineTransmit> mWatchManager;
+ const bool mIsVideo;
+ const RefPtr<PipelineListener> mListener;
+ RefPtr<AudioProxyThread> mAudioProcessing;
+ RefPtr<VideoFrameConverter> mConverter;
+ MediaEventListener mFrameListener;
+ Watchable<RefPtr<dom::MediaStreamTrack>> mDomTrack;
+ // Input port connecting mDomTrack's MediaTrack to mSendTrack.
+ RefPtr<MediaInputPort> mSendPort;
+ // The source track of the mSendTrack. Main thread only.
+ RefPtr<ProcessedMediaTrack> mSendPortSource;
+ // True if a parameter affecting mDescription has changed. To avoid updating
+ // the description unnecessarily. Main thread only.
+ bool mDescriptionInvalidated = true;
+ // Set true once we trigger the async removal of mSendTrack. Set false once
+ // the async removal is done. Main thread only.
+ bool mUnsettingSendTrack = false;
+ // MediaTrack that we send over the network. This allows changing mDomTrack.
+ // Because changing mSendTrack is async and can be racy (when changing from a
+ // track in one graph to a track in another graph), it is set very strictly.
+ // If mSendTrack is null it can be set by UpdateSendState().
+ // If it is non-null it can only be set to null, and only by the
+ // RemoveListener MozPromise handler, as seen in UpdateSendState.
+ RefPtr<ProcessedMediaTrack> mSendTrack;
+ // When this is set and we are active, this track will be used as mSendTrack.
+ // Allows unittests to insert a send track without requiring a dom track or a
+ // graph. Main thread only.
+ Watchable<RefPtr<ProcessedMediaTrack>> mSendTrackOverride;
+ // True when mSendTrack is set, not destroyed and mActive is true. mListener
+ // is attached to mSendTrack when this is true. Main thread only.
+ bool mTransmitting = false;
+};
+
+// A specialization of pipeline for reading from the network and
+// rendering media.
+class MediaPipelineReceive : public MediaPipeline {
+ public:
+ // Set aRtcpTransport to nullptr to use rtcp-mux
+ MediaPipelineReceive(const std::string& aPc,
+ RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread,
+ RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<MediaSessionConduit> aConduit);
+
+ protected:
+ ~MediaPipelineReceive();
+};
+
+// A specialization of pipeline for reading from the network and
+// rendering audio.
+class MediaPipelineReceiveAudio : public MediaPipelineReceive {
+ public:
+ MediaPipelineReceiveAudio(const std::string& aPc,
+ RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread,
+ RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<AudioSessionConduit> aConduit,
+ const RefPtr<dom::MediaStreamTrack>& aTrack,
+ const PrincipalHandle& aPrincipalHandle);
+
+ void Shutdown() override;
+
+ bool IsVideo() const override { return false; }
+
+ void MakePrincipalPrivate_s() override;
+
+ void OnRtpPacketReceived() override;
+
+ private:
+ void UpdateListener();
+
+ // Separate class to allow ref counting
+ class PipelineListener;
+
+ const RefPtr<PipelineListener> mListener;
+ WatchManager<MediaPipelineReceiveAudio> mWatchManager;
+};
+
+// A specialization of pipeline for reading from the network and
+// rendering video.
+class MediaPipelineReceiveVideo : public MediaPipelineReceive {
+ public:
+ MediaPipelineReceiveVideo(const std::string& aPc,
+ RefPtr<MediaTransportHandler> aTransportHandler,
+ RefPtr<AbstractThread> aCallThread,
+ RefPtr<nsISerialEventTarget> aStsThread,
+ RefPtr<VideoSessionConduit> aConduit,
+ const RefPtr<dom::MediaStreamTrack>& aTrack,
+ const PrincipalHandle& aPrincipalHandle);
+
+ void Shutdown() override;
+
+ bool IsVideo() const override { return true; }
+
+ void MakePrincipalPrivate_s() override;
+
+ void OnRtpPacketReceived() override;
+
+ private:
+ void UpdateListener();
+
+ class PipelineRenderer;
+ friend class PipelineRenderer;
+
+ // Separate class to allow ref counting
+ class PipelineListener;
+
+ const RefPtr<PipelineRenderer> mRenderer;
+ const RefPtr<PipelineListener> mListener;
+ WatchManager<MediaPipelineReceiveVideo> mWatchManager;
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp b/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp
new file mode 100644
index 0000000000..1acb73e9f2
--- /dev/null
+++ b/dom/media/webrtc/transportbridge/MediaPipelineFilter.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: softtabstop=2:shiftwidth=2:expandtab
+ * */
+/* 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 "MediaPipelineFilter.h"
+
+#include "api/rtp_headers.h"
+#include "api/rtp_parameters.h"
+#include "mozilla/Logging.h"
+
+// defined in MediaPipeline.cpp
+extern mozilla::LazyLogModule gMediaPipelineLog;
+
+#define DEBUG_LOG(x) MOZ_LOG(gMediaPipelineLog, LogLevel::Debug, x)
+
+namespace mozilla {
+MediaPipelineFilter::MediaPipelineFilter(
+ const std::vector<webrtc::RtpExtension>& aExtMap)
+ : mExtMap(aExtMap) {}
+
+void MediaPipelineFilter::SetRemoteMediaStreamId(
+ const Maybe<std::string>& aMid) {
+ if (aMid != mRemoteMid) {
+ DEBUG_LOG(("MediaPipelineFilter added new remote RTP MID: '%s'.",
+ aMid.valueOr("").c_str()));
+ mRemoteMid = aMid;
+ mRemoteMidBindings.clear();
+ }
+}
+
+bool MediaPipelineFilter::Filter(const webrtc::RTPHeader& header) {
+ DEBUG_LOG(("MediaPipelineFilter inspecting seq# %u SSRC: %u",
+ header.sequenceNumber, header.ssrc));
+
+ auto fromStreamId = [](const std::string& aId) {
+ return Maybe<std::string>(aId.empty() ? Nothing() : Some(aId));
+ };
+
+ //
+ // MID Based Filtering
+ //
+
+ const auto mid = fromStreamId(header.extension.mid);
+
+ // Check to see if a bound SSRC is moved to a new MID
+ if (mRemoteMidBindings.count(header.ssrc) == 1 && mid && mRemoteMid != mid) {
+ mRemoteMidBindings.erase(header.ssrc);
+ }
+ // Bind an SSRC if a matching MID is found
+ if (mid && mRemoteMid == mid) {
+ DEBUG_LOG(("MediaPipelineFilter learned SSRC: %u for MID: '%s'",
+ header.ssrc, mRemoteMid.value().c_str()));
+ mRemoteMidBindings.insert(header.ssrc);
+ }
+ // Check for matching MID
+ if (!mRemoteMidBindings.empty()) {
+ MOZ_ASSERT(mRemoteMid != Nothing());
+ if (mRemoteMidBindings.count(header.ssrc) == 1) {
+ DEBUG_LOG(
+ ("MediaPipelineFilter SSRC: %u matched for MID: '%s'."
+ " passing packet",
+ header.ssrc, mRemoteMid.value().c_str()));
+ return true;
+ }
+ DEBUG_LOG(
+ ("MediaPipelineFilter SSRC: %u did not match bound SSRC(s) for"
+ " MID: '%s'. ignoring packet",
+ header.ssrc, mRemoteMid.value().c_str()));
+ for (const uint32_t ssrc : mRemoteMidBindings) {
+ DEBUG_LOG(("MID %s is associated with SSRC: %u",
+ mRemoteMid.value().c_str(), ssrc));
+ }
+ return false;
+ }
+
+ //
+ // RTP-STREAM-ID based filtering (for tests only)
+ //
+
+ //
+ // Remote SSRC based filtering
+ //
+
+ if (remote_ssrc_set_.count(header.ssrc)) {
+ DEBUG_LOG(
+ ("MediaPipelineFilter SSRC: %u matched remote SSRC set."
+ " passing packet",
+ header.ssrc));
+ return true;
+ }
+ DEBUG_LOG(
+ ("MediaPipelineFilter SSRC: %u did not match any of %zu"
+ " remote SSRCS.",
+ header.ssrc, remote_ssrc_set_.size()));
+
+ //
+ // PT, payload type, last ditch effort filtering
+ //
+
+ if (payload_type_set_.count(header.payloadType)) {
+ DEBUG_LOG(
+ ("MediaPipelineFilter payload-type: %u matched %zu"
+ " unique payload type. learning ssrc. passing packet",
+ header.ssrc, remote_ssrc_set_.size()));
+ // Actual match. We need to update the ssrc map so we can route rtcp
+ // sender reports correctly (these use a different payload-type field)
+ AddRemoteSSRC(header.ssrc);
+ return true;
+ }
+ DEBUG_LOG(
+ ("MediaPipelineFilter payload-type: %u did not match any of %zu"
+ " unique payload-types.",
+ header.payloadType, payload_type_set_.size()));
+ DEBUG_LOG(
+ ("MediaPipelineFilter packet failed to match any criteria."
+ " ignoring packet"));
+ return false;
+}
+
+void MediaPipelineFilter::AddRemoteSSRC(uint32_t ssrc) {
+ remote_ssrc_set_.insert(ssrc);
+}
+
+void MediaPipelineFilter::AddUniquePT(uint8_t payload_type) {
+ payload_type_set_.insert(payload_type);
+}
+
+void MediaPipelineFilter::Update(const MediaPipelineFilter& filter_update) {
+ // We will not stomp the remote_ssrc_set_ if the update has no ssrcs,
+ // because we don't want to unlearn any remote ssrcs unless the other end
+ // has explicitly given us a new set.
+ if (!filter_update.remote_ssrc_set_.empty()) {
+ remote_ssrc_set_ = filter_update.remote_ssrc_set_;
+ }
+ // We don't want to overwrite the learned binding unless we have changed MIDs
+ // or the update contains a MID binding.
+ if (!filter_update.mRemoteMidBindings.empty() ||
+ (filter_update.mRemoteMid && filter_update.mRemoteMid != mRemoteMid)) {
+ mRemoteMid = filter_update.mRemoteMid;
+ mRemoteMidBindings = filter_update.mRemoteMidBindings;
+ }
+ payload_type_set_ = filter_update.payload_type_set_;
+
+ // Use extmapping from new filter
+ mExtMap = filter_update.mExtMap;
+}
+
+} // end namespace mozilla
diff --git a/dom/media/webrtc/transportbridge/MediaPipelineFilter.h b/dom/media/webrtc/transportbridge/MediaPipelineFilter.h
new file mode 100644
index 0000000000..9b40bceda8
--- /dev/null
+++ b/dom/media/webrtc/transportbridge/MediaPipelineFilter.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: softtabstop=2:shiftwidth=2:expandtab
+ * */
+/* 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 mediapipelinefilter_h__
+#define mediapipelinefilter_h__
+
+#include <cstddef>
+#include <stdint.h>
+#include <string>
+
+#include <set>
+#include <vector>
+
+#include "mozilla/Maybe.h"
+
+namespace webrtc {
+struct RTPHeader;
+struct RtpExtension;
+} // namespace webrtc
+
+namespace mozilla {
+
+// TODO @@NG update documentation after initial review
+
+// A class that handles the work of filtering RTP packets that arrive at a
+// MediaPipeline. This is primarily important for the use of BUNDLE (ie;
+// multiple m-lines share the same RTP stream). There are three ways that this
+// can work;
+//
+// 1) In our SDP, we include a media-level extmap parameter with a unique
+// integer of our choosing, with the hope that the other side will include
+// this value in a header in the first few RTP packets it sends us. This
+// allows us to perform correlation in cases where the other side has not
+// informed us of the ssrcs it will be sending (either because it did not
+// include them in its SDP, or their SDP has not arrived yet)
+// and also gives us the opportunity to learn SSRCs from packets so adorned.
+//
+// 2) If the remote endpoint includes SSRC media-level attributes in its SDP,
+// we can simply use this information to populate the filter. The only
+// shortcoming here is when RTP packets arrive before the answer does. See
+// above.
+//
+// 3) As a fallback, we can try to use payload type IDs to perform correlation,
+// but only when the type id is unique to this media section.
+// This too allows us to learn about SSRCs (mostly useful for filtering
+// sender reports later).
+class MediaPipelineFilter {
+ public:
+ MediaPipelineFilter() = default;
+ explicit MediaPipelineFilter(
+ const std::vector<webrtc::RtpExtension>& aExtMap);
+
+ // Checks whether this packet passes the filter, possibly updating the filter
+ // in the process (if the MID or payload types are used, they can teach
+ // the filter about ssrcs)
+ bool Filter(const webrtc::RTPHeader& header);
+
+ void AddRemoteSSRC(uint32_t ssrc);
+
+ void SetRemoteMediaStreamId(const Maybe<std::string>& aMid);
+
+ // When a payload type id is unique to our media section, add it here.
+ void AddUniquePT(uint8_t payload_type);
+
+ void Update(const MediaPipelineFilter& filter_update);
+
+ std::vector<webrtc::RtpExtension> GetExtmap() const { return mExtMap; }
+
+ private:
+ // The number of filters we manage here is quite small, so I am optimizing
+ // for readability.
+ std::set<uint32_t> remote_ssrc_set_;
+ std::set<uint8_t> payload_type_set_;
+ Maybe<std::string> mRemoteMid;
+ std::set<uint32_t> mRemoteMidBindings;
+ // RID extension can be set by tests and is sticky, the rest of
+ // the mapping is not.
+ std::vector<webrtc::RtpExtension> mExtMap;
+};
+
+} // end namespace mozilla
+
+#endif // mediapipelinefilter_h__
diff --git a/dom/media/webrtc/transportbridge/RtpLogger.cpp b/dom/media/webrtc/transportbridge/RtpLogger.cpp
new file mode 100644
index 0000000000..aac1fad197
--- /dev/null
+++ b/dom/media/webrtc/transportbridge/RtpLogger.cpp
@@ -0,0 +1,67 @@
+/* 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: nohlmeier@mozilla.com
+
+#include "RtpLogger.h"
+#include "mozilla/Logging.h"
+
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+#ifdef _WIN32
+# include <time.h>
+# include <sys/timeb.h>
+#else
+# include <sys/time.h>
+#endif
+
+// Logging context
+using namespace mozilla;
+
+mozilla::LazyLogModule gRtpLoggerLog("RtpLogger");
+
+namespace mozilla {
+
+bool RtpLogger::IsPacketLoggingOn() {
+ return MOZ_LOG_TEST(gRtpLoggerLog, LogLevel::Debug);
+}
+
+void RtpLogger::LogPacket(const MediaPacket& packet, bool input,
+ std::string desc) {
+ if (MOZ_LOG_TEST(gRtpLoggerLog, LogLevel::Debug)) {
+ bool isRtp = (packet.type() == MediaPacket::RTP);
+ std::stringstream ss;
+ /* This creates text2pcap compatible format, e.g.:
+ * RTCP_PACKET O 10:36:26.864934 000000 80 c8 00 06 6d ...
+ */
+ ss << (input ? "I " : "O ");
+ std::time_t t = std::time(nullptr);
+ std::tm tm = *std::localtime(&t);
+ char buf[9];
+ if (0 < strftime(buf, sizeof(buf), "%H:%M:%S", &tm)) {
+ ss << buf;
+ }
+ ss << std::setfill('0');
+#ifdef _WIN32
+ struct timeb tb;
+ ftime(&tb);
+ ss << "." << (tb.millitm) << " ";
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ss << "." << (tv.tv_usec) << " ";
+#endif
+ ss << " 000000";
+ ss << std::hex << std::setfill('0');
+ for (size_t i = 0; i < packet.len(); ++i) {
+ ss << " " << std::setw(2) << (int)packet.data()[i];
+ }
+ MOZ_LOG(gRtpLoggerLog, LogLevel::Debug,
+ ("%s%s%s", desc.c_str(), (isRtp ? " RTP_PACKET " : " RTCP_PACKET "),
+ ss.str().c_str()));
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/transportbridge/RtpLogger.h b/dom/media/webrtc/transportbridge/RtpLogger.h
new file mode 100644
index 0000000000..fcfaede6e2
--- /dev/null
+++ b/dom/media/webrtc/transportbridge/RtpLogger.h
@@ -0,0 +1,28 @@
+/* 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: nohlmeier@mozilla.com
+
+#ifndef rtplogger_h__
+#define rtplogger_h__
+
+#include "transport/mediapacket.h"
+
+namespace mozilla {
+
+/* This class logs RTP and RTCP packets in hex in a format compatible to
+ * text2pcap.
+ * Example to convert the MOZ log file into a PCAP file:
+ * egrep '(RTP_PACKET|RTCP_PACKET)' moz.log | \
+ * text2pcap -D -n -l 1 -i 17 -u 1234,1235 -t '%H:%M:%S.' - rtp.pcap
+ */
+class RtpLogger {
+ public:
+ static bool IsPacketLoggingOn();
+ static void LogPacket(const MediaPacket& packet, bool input,
+ std::string desc);
+};
+
+} // namespace mozilla
+#endif
diff --git a/dom/media/webrtc/transportbridge/moz.build b/dom/media/webrtc/transportbridge/moz.build
new file mode 100644
index 0000000000..290dd26a0a
--- /dev/null
+++ b/dom/media/webrtc/transportbridge/moz.build
@@ -0,0 +1,27 @@
+# -*- 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("/dom/media/webrtc/third_party_build/webrtc.mozbuild")
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/dom/media",
+ "/dom/media/webrtc",
+ "/ipc/chromium/src",
+ "/media/libyuv/libyuv/include",
+ "/media/webrtc",
+ "/third_party/libsrtp/src/crypto/include",
+ "/third_party/libsrtp/src/include",
+ "/third_party/libwebrtc",
+ "/third_party/libwebrtc/third_party/abseil-cpp",
+]
+
+UNIFIED_SOURCES += [
+ "MediaPipeline.cpp",
+ "MediaPipelineFilter.cpp",
+ "RtpLogger.cpp",
+]
+
+FINAL_LIBRARY = "xul"