diff options
Diffstat (limited to 'media/mtransport/third_party/nICEr')
78 files changed, 23744 insertions, 0 deletions
diff --git a/media/mtransport/third_party/nICEr/IMPORT_FILES b/media/mtransport/third_party/nICEr/IMPORT_FILES new file mode 100644 index 000000000..4b30b2141 --- /dev/null +++ b/media/mtransport/third_party/nICEr/IMPORT_FILES @@ -0,0 +1,71 @@ + # Crypto + ./src/crypto/nr_crypto.c + ./src/crypto/nr_crypto.h + #./src/crypto/nr_crypto_openssl.c + #./src/crypto/nr_crypto_openssl.h + + # ICE + ./src/ice/ice_candidate.c + ./src/ice/ice_candidate.h + ./src/ice/ice_candidate_pair.c + ./src/ice/ice_candidate_pair.h + ./src/ice/ice_codeword.h + ./src/ice/ice_component.c + ./src/ice/ice_component.h + ./src/ice/ice_ctx.c + ./src/ice/ice_ctx.h + ./src/ice/ice_handler.h + ./src/ice/ice_media_stream.c + ./src/ice/ice_media_stream.h + ./src/ice/ice_parser.c + ./src/ice/ice_peer_ctx.c + ./src/ice/ice_peer_ctx.h + ./src/ice/ice_reg.h + ./src/ice/ice_socket.c + ./src/ice/ice_socket.h + + # Net + ./src/net/nr_socket.c + ./src/net/nr_socket.h + #./src/net/nr_socket_local.c + ./src/net/nr_socket_local.h + ./src/net/transport_addr.c + ./src/net/transport_addr.h + ./src/net/transport_addr_reg.c + ./src/net/transport_addr_reg.h + + # STUN + ./src/stun/addrs.c + ./src/stun/addrs.h + ./src/stun/ifaddrs-android.c + ./src/stun/ifaddrs-android.h + ./src/stun/nr_socket_turn.c + ./src/stun/nr_socket_turn.h + ./src/stun/stun.h + ./src/stun/stun_build.c + ./src/stun/stun_build.h + ./src/stun/stun_client_ctx.c + ./src/stun/stun_client_ctx.h + ./src/stun/stun_codec.c + ./src/stun/stun_codec.h + ./src/stun/stun_hint.c + ./src/stun/stun_hint.h + ./src/stun/stun_msg.c + ./src/stun/stun_msg.h + ./src/stun/stun_proc.c + ./src/stun/stun_proc.h + ./src/stun/stun_reg.h + ./src/stun/stun_server_ctx.c + ./src/stun/stun_server_ctx.h + ./src/stun/stun_util.c + ./src/stun/stun_util.h + ./src/stun/turn_client_ctx.c + ./src/stun/turn_client_ctx.h + + # Util + ./src/util/cb_args.c + ./src/util/cb_args.h + ./src/util/ice_util.c + ./src/util/ice_util.h + ./src/util/mbslen.c + ./src/util/mbslen.h diff --git a/media/mtransport/third_party/nICEr/README b/media/mtransport/third_party/nICEr/README new file mode 100644 index 000000000..390203dec --- /dev/null +++ b/media/mtransport/third_party/nICEr/README @@ -0,0 +1,74 @@ + nICEr 1.0 + +PREREQUISITES: +------------- +You must first obtain and build the following packages: + +* nrappkit + - http://nrappkit.sourceforge.net/ + +* OpenSSL-0.9.8g + - http://www.openssl.org/source/openssl-0.9.8g.tar.gz + + +For best results, the "ice-<version>" directory should be at +the same level as the "nrappkit" and "openssl-0.9.8g" +directories. I.e., the directory structure should look like: + + nrappkit/ + ice-<version>/ + openssl/ + include/ + lib/VC/ + + +BUILDING ON UNIX: +---------------- +Once the prerequisite packages are built, 'cd' to the +relevant build directory, one of: + + ice-<version>/make/darwin + ice-<version>/make/linux-fedora + ice-<version>/make/ubuntu + +and simply do a "make". + + +BUILDING ON WINDOWS: +------------------- +The Visual C++ project files are configured to expect the +directory structure described above. + +Note: Binary Windows builds of OpenSSL can be found at: + http://www.slproweb.com/products/Win32OpenSSL.html + +Once the prerequisite packages are built, open the VC++ 9.0 +solution file: ICE/make/win32/ice.sln and build the solution. +Note: Since the VC++ project/solution files are version 9.0, +Visual Studio 2008 is required. + + +STATUS: +------ +The ICE code has been tested on the following platforms: +-- Fedora Core 4 (Intel 32-bit) +-- Fedora Core 6 (Intel 32-bit) +-- Ubuntu 6.10 +-- MacOSX 10.4.9 +-- Windows Vista (Home Premium) +-- Windows XP Pro +-- Windows 2000 SP4 + + +KNOWN ISSUES: +------------ +-- TURN SET-ACTIVE-DESTINATION mode not yet supported. + +-- Problems may exist with the TURN client implementation; the TURN code + has received minimal testing due to the unavailability of a real + TURN server to test against. + +-- The ICE-Lite implementation is not complete. + +-- The new "impatient" timeout has not yet been thoroughly tested. + diff --git a/media/mtransport/third_party/nICEr/README_MOZILLA b/media/mtransport/third_party/nICEr/README_MOZILLA new file mode 100644 index 000000000..8e058ac7d --- /dev/null +++ b/media/mtransport/third_party/nICEr/README_MOZILLA @@ -0,0 +1,11 @@ +This code was imported from: + +https://svn.resiprocate.org/rep/resiprocate/main/nICEr + +SVN revision 9873 + +Some minor changes have been made to improve portability as well +as to add trickle ICE. These changes are being submitted for +upstreaming and can be found in upstream.diff. + + diff --git a/media/mtransport/third_party/nICEr/nicer.gyp b/media/mtransport/third_party/nICEr/nicer.gyp new file mode 100644 index 000000000..9df4a4b28 --- /dev/null +++ b/media/mtransport/third_party/nICEr/nicer.gyp @@ -0,0 +1,254 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# nrappkit.gyp +# +# +{ + 'variables' : { + 'build_with_gonk%': 0, + 'have_ethtool_cmd_speed_hi%': 1 + }, + 'targets' : [ + { + 'target_name' : 'nicer', + 'type' : 'static_library', + + 'include_dirs' : [ + ## EXTERNAL + # nrappkit + '../nrappkit/src/event', + '../nrappkit/src/log', + '../nrappkit/src/plugin', + '../nrappkit/src/registry', + '../nrappkit/src/share', + '../nrappkit/src/stats', + '../nrappkit/src/util', + '../nrappkit/src/util/libekr', + '../nrappkit/src/port/generic/include', + + # INTERNAL + "./src/crypto", + "./src/ice", + "./src/net", + "./src/stun", + "./src/util", + ], + + 'sources' : [ + # Crypto + "./src/crypto/nr_crypto.c", + "./src/crypto/nr_crypto.h", + #"./src/crypto/nr_crypto_openssl.c", + #"./src/crypto/nr_crypto_openssl.h", + + # ICE + "./src/ice/ice_candidate.c", + "./src/ice/ice_candidate.h", + "./src/ice/ice_candidate_pair.c", + "./src/ice/ice_candidate_pair.h", + "./src/ice/ice_codeword.h", + "./src/ice/ice_component.c", + "./src/ice/ice_component.h", + "./src/ice/ice_ctx.c", + "./src/ice/ice_ctx.h", + "./src/ice/ice_handler.h", + "./src/ice/ice_media_stream.c", + "./src/ice/ice_media_stream.h", + "./src/ice/ice_parser.c", + "./src/ice/ice_peer_ctx.c", + "./src/ice/ice_peer_ctx.h", + "./src/ice/ice_reg.h", + "./src/ice/ice_socket.c", + "./src/ice/ice_socket.h", + + # Net + "./src/net/nr_resolver.c", + "./src/net/nr_resolver.h", + "./src/net/nr_socket_wrapper.c", + "./src/net/nr_socket_wrapper.h", + "./src/net/nr_proxy_tunnel.c", + "./src/net/nr_proxy_tunnel.h", + "./src/net/nr_socket.c", + "./src/net/nr_socket.h", + #"./src/net/nr_socket_local.c", + "./src/net/nr_socket_local.h", + "./src/net/nr_socket_multi_tcp.c", + "./src/net/nr_socket_multi_tcp.h", + "./src/net/transport_addr.c", + "./src/net/transport_addr.h", + "./src/net/transport_addr_reg.c", + "./src/net/transport_addr_reg.h", + "./src/net/local_addr.c", + "./src/net/local_addr.h", + "./src/net/nr_interface_prioritizer.c", + "./src/net/nr_interface_prioritizer.h", + + # STUN + "./src/stun/addrs.c", + "./src/stun/addrs.h", + "./src/stun/ifaddrs-android.c", + "./src/stun/ifaddrs-android.h", + "./src/stun/nr_socket_turn.c", + "./src/stun/nr_socket_turn.h", + "./src/stun/nr_socket_buffered_stun.c", + "./src/stun/nr_socket_buffered_stun.h", + "./src/stun/stun.h", + "./src/stun/stun_build.c", + "./src/stun/stun_build.h", + "./src/stun/stun_client_ctx.c", + "./src/stun/stun_client_ctx.h", + "./src/stun/stun_codec.c", + "./src/stun/stun_codec.h", + "./src/stun/stun_hint.c", + "./src/stun/stun_hint.h", + "./src/stun/stun_msg.c", + "./src/stun/stun_msg.h", + "./src/stun/stun_proc.c", + "./src/stun/stun_proc.h", + "./src/stun/stun_reg.h", + "./src/stun/stun_server_ctx.c", + "./src/stun/stun_server_ctx.h", + "./src/stun/stun_util.c", + "./src/stun/stun_util.h", + "./src/stun/turn_client_ctx.c", + "./src/stun/turn_client_ctx.h", + + # Util + "./src/util/cb_args.c", + "./src/util/cb_args.h", + "./src/util/ice_util.c", + "./src/util/ice_util.h", + "./src/util/mbslen.c", + "./src/util/mbslen.h", + + + ], + + 'defines' : [ + 'SANITY_CHECKS', + 'USE_TURN', + 'USE_ICE', + 'USE_RFC_3489_BACKWARDS_COMPATIBLE', + 'USE_STUND_0_96', + 'USE_STUN_PEDANTIC', + 'USE_TURN', + 'NR_SOCKET_IS_VOID_PTR', + 'restrict=', + 'R_PLATFORM_INT_TYPES=<stdint.h>', + 'R_DEFINED_INT2=int16_t', + 'R_DEFINED_UINT2=uint16_t', + 'R_DEFINED_INT4=int32_t', + 'R_DEFINED_UINT4=uint32_t', + 'R_DEFINED_INT8=int64_t', + 'R_DEFINED_UINT8=uint64_t', + ], + + 'conditions' : [ + ## Mac and BSDs + [ 'OS == "mac" or OS == "ios"', { + 'defines' : [ + 'DARWIN', + ], + }], + [ 'os_bsd == 1', { + 'defines' : [ + 'BSD', + ], + }], + [ 'OS == "mac" or OS == "ios" or os_bsd == 1', { + 'cflags_mozilla': [ + '-Wall', + '-Wno-parentheses', + '-Wno-strict-prototypes', + '-Wmissing-prototypes', + ], + 'defines' : [ + 'HAVE_LIBM=1', + 'HAVE_STRDUP=1', + 'HAVE_STRLCPY=1', + 'HAVE_SYS_TIME_H=1', + 'HAVE_VFPRINTF=1', + 'NEW_STDIO' + 'RETSIGTYPE=void', + 'TIME_WITH_SYS_TIME_H=1', + '__UNUSED__=__attribute__((unused))', + ], + + 'include_dirs': [ + '../nrappkit/src/port/darwin/include' + ], + + 'sources': [ + ], + }], + + ## Win + [ 'OS == "win"', { + 'defines' : [ + 'WIN32', + 'USE_ICE', + 'USE_TURN', + 'USE_RFC_3489_BACKWARDS_COMPATIBLE', + 'USE_STUND_0_96', + 'USE_STUN_PEDANTIC', + '_CRT_SECURE_NO_WARNINGS', + '__UNUSED__=', + 'HAVE_STRDUP', + 'NO_REG_RPC' + ], + + 'include_dirs': [ + '../nrappkit/src/port/win32/include' + ], + }], + ## Linux/Android + [ '(OS == "linux") or (OS=="android")', { + 'cflags_mozilla': [ + '-Wall', + '-Wno-parentheses', + '-Wno-strict-prototypes', + '-Wmissing-prototypes', + ], + 'defines' : [ + 'LINUX', + 'HAVE_LIBM=1', + 'HAVE_STRDUP=1', + 'HAVE_STRLCPY=1', + 'HAVE_SYS_TIME_H=1', + 'HAVE_VFPRINTF=1', + 'NEW_STDIO' + 'RETSIGTYPE=void', + 'TIME_WITH_SYS_TIME_H=1', + '__UNUSED__=__attribute__((unused))', + ], + + 'include_dirs': [ + '../nrappkit/src/port/linux/include' + ], + + 'sources': [ + ], + }], + ['moz_widget_toolkit_gonk==1', { + 'defines' : [ + 'WEBRTC_GONK', + 'NO_REG_RPC', + ], + }], + # Gonk has its own nr_stun_get_addrs implementation. + ['build_with_gonk==1', { + 'defines': [ + "USE_PLATFORM_NR_STUN_GET_ADDRS", + ] + }], + ['have_ethtool_cmd_speed_hi==0', { + 'defines': [ + "DONT_HAVE_ETHTOOL_SPEED_HI", + ] + }] + ], + }] +} + diff --git a/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c new file mode 100644 index 000000000..088ebf045 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.c @@ -0,0 +1,71 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: nr_crypto.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <nr_api.h> +#include "nr_crypto.h" + +static int nr_ice_crypto_dummy_random_bytes(UCHAR *buf, int len) + { + fprintf(stderr,"Need to define crypto API implementation\n"); + + exit(1); + } + +static int nr_ice_crypto_dummy_hmac_sha1(UCHAR *key, int key_l, UCHAR *buf, int buf_l, UCHAR digest[20]) + { + fprintf(stderr,"Need to define crypto API implementation\n"); + + exit(1); + } + +static int nr_ice_crypto_dummy_md5(UCHAR *buf, int buf_l, UCHAR digest[16]) + { + fprintf(stderr,"Need to define crypto API implementation\n"); + + exit(1); + } + +static nr_ice_crypto_vtbl nr_ice_crypto_dummy_vtbl= { + nr_ice_crypto_dummy_random_bytes, + nr_ice_crypto_dummy_hmac_sha1, + nr_ice_crypto_dummy_md5 +}; + + + +nr_ice_crypto_vtbl *nr_crypto_vtbl=&nr_ice_crypto_dummy_vtbl; + + diff --git a/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h new file mode 100644 index 000000000..4c71786f0 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/crypto/nr_crypto.h @@ -0,0 +1,52 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _nr_crypto_h +#define _nr_crypto_h + + +typedef struct nr_ice_crypto_vtbl_ { + int (*random_bytes)(UCHAR *buf, int len); + int (*hmac_sha1)(UCHAR *key, int key_l, UCHAR *buf, int buf_l, UCHAR digest[20]); + int (*md5)(UCHAR *buf, int buf_l, UCHAR digest[16]); +} nr_ice_crypto_vtbl; + +extern nr_ice_crypto_vtbl *nr_crypto_vtbl; + +#define nr_crypto_random_bytes(a,b) nr_crypto_vtbl->random_bytes(a,b) +#define nr_crypto_hmac_sha1(a,b,c,d,e) nr_crypto_vtbl->hmac_sha1(a,b,c,d,e) +#define nr_crypto_md5(a,b,c) nr_crypto_vtbl->md5(a,b,c) + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c new file mode 100644 index 000000000..e48b7b5e1 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c @@ -0,0 +1,979 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_candidate.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <csi_platform.h> +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <sys/queue.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include "nr_api.h" +#include "registry.h" +#include "nr_socket.h" +#include "async_timer.h" + +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "turn_client_ctx.h" +#include "ice_ctx.h" +#include "ice_candidate.h" +#include "ice_codeword.h" +#include "ice_reg.h" +#include "ice_util.h" +#include "nr_socket_turn.h" +#include "nr_socket.h" +#include "nr_socket_multi_tcp.h" + +static int next_automatic_preference = 127; + +static int nr_ice_candidate_initialize2(nr_ice_candidate *cand); +static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand); +static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand); +static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg); +#ifdef USE_TURN +static int nr_ice_start_relay_turn(nr_ice_candidate *cand); +static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg); +static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr); +#endif /* USE_TURN */ + +void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand) + { + char as_string[1024]; + + snprintf(as_string, + sizeof(as_string), + "%s(%s)", + cand->addr.as_string, + cand->label); + + nr_ice_compute_codeword(as_string,strlen(as_string),cand->codeword); + } + +char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0}; +char *nr_ice_candidate_tcp_type_names[]={0,"active","passive","so",0}; + +static const char *nr_ctype_name(nr_ice_candidate_type ctype) { + assert(ctype<CTYPE_MAX && ctype>0); + if (ctype <= 0 || ctype >= CTYPE_MAX) { + return "ERROR"; + } + return nr_ice_candidate_type_names[ctype]; +} + +static const char *nr_tcp_type_name(nr_socket_tcp_type tcp_type) { + assert(tcp_type<TCP_TYPE_MAX && tcp_type>0); + if (tcp_type <= 0 || tcp_type >= TCP_TYPE_MAX) { + return "ERROR"; + } + return nr_ice_candidate_tcp_type_names[tcp_type]; +} + +static int nr_ice_candidate_format_stun_label(char *label, size_t size, nr_ice_candidate *cand) + { + int _status; + + *label = 0; + switch(cand->stun_server->type) { + case NR_ICE_STUN_SERVER_TYPE_ADDR: + snprintf(label, size, "%s(%s|%s)", nr_ctype_name(cand->type), cand->base.as_string, + cand->stun_server->u.addr.as_string); + break; + case NR_ICE_STUN_SERVER_TYPE_DNSNAME: + snprintf(label, size, "%s(%s|%s:%u)", nr_ctype_name(cand->type), cand->base.as_string, + cand->stun_server->u.dnsname.host, cand->stun_server->u.dnsname.port); + break; + default: + assert(0); + ABORT(R_BAD_ARGS); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_candidate_create(nr_ice_ctx *ctx,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp) + { + assert(!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) || ctype == RELAYED); + nr_ice_candidate *cand=0; + nr_ice_candidate *tmp=0; + int r,_status; + char label[512]; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + cand->state=NR_ICE_CAND_STATE_CREATED; + cand->ctx=ctx; + cand->isock=isock; + cand->osock=osock; + cand->type=ctype; + cand->tcp_type=tcp_type; + cand->stun_server=stun_server; + cand->component_id=component_id; + cand->component=comp; + cand->stream=comp->stream; + + /* Extract the addr as the base */ + if(r=nr_socket_getaddr(cand->isock->sock,&cand->base)) + ABORT(r); + + switch(ctype) { + case HOST: + snprintf(label, sizeof(label), "host(%s)", cand->base.as_string); + break; + + case SERVER_REFLEXIVE: + if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand)) + ABORT(r); + break; + + case RELAYED: + if(r=nr_ice_candidate_format_stun_label(label, sizeof(label), cand)) + ABORT(r); + break; + + case PEER_REFLEXIVE: + snprintf(label, sizeof(label), "prflx"); + break; + + default: + assert(0); /* Can't happen */ + ABORT(R_BAD_ARGS); + } + + if (tcp_type) { + const char* ttype = nr_tcp_type_name(tcp_type); + const int tlen = strlen(ttype)+1; /* plus space */ + const size_t llen=strlen(label); + if (snprintf(label+llen, sizeof(label)-llen, " %s", ttype) != tlen) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): truncated tcp type added to buffer", + ctx->label); + } + } + + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + if(r=nr_ice_get_foundation(ctx,cand)) + ABORT(r); + if(r=nr_ice_candidate_compute_priority(cand)) + ABORT(r); + + TAILQ_FOREACH(tmp,&isock->candidates,entry_sock){ + if(cand->priority==tmp->priority){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): duplicate priority %u candidate %s and candidate %s", + ctx->label,cand->priority,cand->label,tmp->label); + } + } + + if(ctype==RELAYED) + cand->u.relayed.turn_sock=osock; + + + /* Add the candidate to the isock list*/ + TAILQ_INSERT_TAIL(&isock->candidates,cand,entry_sock); + + nr_ice_candidate_compute_codeword(cand); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): created candidate %s with type %s", + ctx->label,cand->codeword,cand->label,nr_ctype_name(ctype)); + + *candp=cand; + + _status=0; + abort: + if (_status){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Failed to create candidate of type %s", ctx->label,nr_ctype_name(ctype)); + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + + +/* Create a peer reflexive candidate */ +int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + nr_ice_candidate_type ctype=PEER_REFLEXIVE; + int r,_status; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + cand->state=NR_ICE_CAND_STATE_INITIALIZED; + cand->ctx=ctx; + cand->type=ctype; + cand->component_id=comp->component_id; + cand->component=comp; + cand->stream=comp->stream; + + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): creating candidate with type %s", + ctx->label,label,nr_ctype_name(ctype)); + + if(r=nr_transport_addr_copy(&cand->base,addr)) + ABORT(r); + if(r=nr_transport_addr_copy(&cand->addr,addr)) + ABORT(r); + /* Bogus foundation */ + if(!(cand->foundation=r_strdup(cand->addr.as_string))) + ABORT(R_NO_MEMORY); + + nr_ice_candidate_compute_codeword(cand); + + *candp=cand; + + _status=0; + abort: + if (_status){ + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + +static void nr_ice_candidate_mark_done(nr_ice_candidate *cand, int state) + { + if (!cand || !cand->done_cb) { + assert(0); + return; + } + + /* If this is a relay candidate, there's likely to be a srflx that is + * piggybacking on it. Make sure it is marked done too. */ + if ((cand->type == RELAYED) && cand->u.relayed.srvflx_candidate) { + nr_ice_candidate *srflx=cand->u.relayed.srvflx_candidate; + if (state == NR_ICE_CAND_STATE_INITIALIZED && + nr_turn_client_get_mapped_address(cand->u.relayed.turn, + &srflx->addr)) { + r_log(LOG_ICE, LOG_WARNING, "ICE(%s)/CAND(%s): Failed to get mapped address from TURN allocate response, srflx failed.", cand->ctx->label, cand->label); + nr_ice_candidate_mark_done(srflx, NR_ICE_CAND_STATE_FAILED); + } else { + nr_ice_candidate_mark_done(srflx, state); + } + } + + NR_async_cb done_cb=cand->done_cb; + cand->done_cb=0; + cand->state=state; + /* This might destroy cand! */ + done_cb(0,0,cand->cb_arg); + } + +int nr_ice_candidate_destroy(nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + + if(!candp || !*candp) + return(0); + + cand=*candp; + + if (cand->state == NR_ICE_CAND_STATE_INITIALIZING) { + /* Make sure the ICE ctx isn't still waiting around for this candidate + * to init. */ + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + + switch(cand->type){ + case HOST: + break; +#ifdef USE_TURN + case RELAYED: + if (cand->u.relayed.turn_handle) + nr_ice_socket_deregister(cand->isock, cand->u.relayed.turn_handle); + if (cand->u.relayed.srvflx_candidate) + cand->u.relayed.srvflx_candidate->u.srvrflx.relay_candidate=0; + nr_turn_client_ctx_destroy(&cand->u.relayed.turn); + nr_socket_destroy(&cand->u.relayed.turn_sock); + break; +#endif /* USE_TURN */ + case SERVER_REFLEXIVE: + if (cand->u.srvrflx.stun_handle) + nr_ice_socket_deregister(cand->isock, cand->u.srvrflx.stun_handle); + if (cand->u.srvrflx.relay_candidate) + cand->u.srvrflx.relay_candidate->u.relayed.srvflx_candidate=0; + nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); + break; + default: + break; + } + + NR_async_timer_cancel(cand->delay_timer); + NR_async_timer_cancel(cand->ready_cb_timer); + if(cand->resolver_handle){ + nr_resolver_cancel(cand->ctx->resolver,cand->resolver_handle); + } + + RFREE(cand->foundation); + RFREE(cand->label); + RFREE(cand); + + return(0); + } + +/* This algorithm is not super-fast, but I don't think we need a hash + table just yet and it produces a small foundation string */ +static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand) + { + nr_ice_foundation *foundation; + int i=0; + char fnd[20]; + int _status; + + foundation=STAILQ_FIRST(&ctx->foundations); + while(foundation){ + if(nr_transport_addr_cmp(&cand->base,&foundation->addr,NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + goto next; + if(cand->type != foundation->type) + goto next; + if(cand->stun_server != foundation->stun_server) + goto next; + + snprintf(fnd,sizeof(fnd),"%d",i); + if(!(cand->foundation=r_strdup(fnd))) + ABORT(R_NO_MEMORY); + return(0); + + next: + foundation=STAILQ_NEXT(foundation,entry); + i++; + } + + if(!(foundation=RCALLOC(sizeof(nr_ice_foundation)))) + ABORT(R_NO_MEMORY); + nr_transport_addr_copy(&foundation->addr,&cand->base); + foundation->type=cand->type; + foundation->stun_server=cand->stun_server; + STAILQ_INSERT_TAIL(&ctx->foundations,foundation,entry); + + snprintf(fnd,sizeof(fnd),"%d",i); + if(!(cand->foundation=r_strdup(fnd))) + ABORT(R_NO_MEMORY); + + _status=0; + abort: + return(_status); + } + +int nr_ice_candidate_compute_priority(nr_ice_candidate *cand) + { + UCHAR type_preference; + UCHAR interface_preference; + UCHAR stun_priority; + UCHAR direction_priority=0; + int r,_status; + + if (cand->base.protocol != IPPROTO_UDP && cand->base.protocol != IPPROTO_TCP){ + r_log(LOG_ICE,LOG_ERR,"Unknown protocol type %u", + (unsigned int)cand->base.protocol); + ABORT(R_INTERNAL); + } + + switch(cand->type){ + case HOST: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_HOST_TCP,&type_preference)) + ABORT(r); + } + stun_priority=0; + break; + case RELAYED: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_RELAYED_TCP,&type_preference)) + ABORT(r); + } + stun_priority=31-cand->stun_server->id; + break; + case SERVER_REFLEXIVE: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP,&type_preference)) + ABORT(r); + } + stun_priority=31-cand->stun_server->id; + break; + case PEER_REFLEXIVE: + if(cand->base.protocol == IPPROTO_UDP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX,&type_preference)) + ABORT(r); + } else if(cand->base.protocol == IPPROTO_TCP) { + if(r=NR_reg_get_uchar(NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP,&type_preference)) + ABORT(r); + } + stun_priority=0; + break; + default: + ABORT(R_INTERNAL); + } + + if(cand->base.protocol == IPPROTO_TCP){ + switch (cand->tcp_type) { + case TCP_TYPE_ACTIVE: + if (cand->type == HOST) + direction_priority=6; + else + direction_priority=4; + break; + case TCP_TYPE_PASSIVE: + if (cand->type == HOST) + direction_priority=4; + else + direction_priority=2; + break; + case TCP_TYPE_SO: + if (cand->type == HOST) + direction_priority=2; + else + direction_priority=6; + break; + case TCP_TYPE_NONE: + break; + case TCP_TYPE_MAX: + default: + assert(0); + ABORT(R_INTERNAL); + } + } + + if(type_preference > 126) + r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference); + + if(!cand->ctx->interface_prioritizer) { + /* Prioritizer is not set, read from registry */ + if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname, + &interface_preference)) { + if (r==R_NOT_FOUND) { + if (next_automatic_preference == 1) { + r_log(LOG_ICE,LOG_ERR,"Out of preference values. Can't assign one for interface %s",cand->base.ifname); + ABORT(R_NOT_FOUND); + } + r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname, + next_automatic_preference); + if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){ + ABORT(r); + } + interface_preference=next_automatic_preference << 1; + next_automatic_preference--; + if (cand->base.ip_version == NR_IPV6) { + /* Prefer IPV6 over IPV4 on the same interface. */ + interface_preference += 1; + } + } + else { + ABORT(r); + } + } + } + else { + char key_of_interface[MAXIFNAME + 41]; + nr_transport_addr addr; + + if(r=nr_socket_getaddr(cand->isock->sock, &addr)) + ABORT(r); + + if(r=nr_transport_addr_fmt_ifname_addr_string(&addr,key_of_interface, + sizeof(key_of_interface))) { + ABORT(r); + } + if(r=nr_interface_prioritizer_get_priority(cand->ctx->interface_prioritizer, + key_of_interface,&interface_preference)) { + ABORT(r); + } + } + + assert(stun_priority < 32); + assert(direction_priority < 8); + + cand->priority= + (type_preference << 24) | + (interface_preference << 16) | + (direction_priority << 13) | + (stun_priority << 8) | + (256 - cand->component_id); + + /* S 4.1.2 */ + assert(cand->priority>=1&&cand->priority<=2147483647); + + _status=0; + abort: + return(_status); + } + +static void nr_ice_candidate_fire_ready_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand = cb_arg; + + cand->ready_cb_timer = 0; + cand->ready_cb(0, 0, cand->ready_cb_arg); + } + +int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg) + { + int r,_status; + int protocol=NR_RESOLVE_PROTOCOL_STUN; + cand->done_cb=ready_cb; + cand->cb_arg=cb_arg; + cand->state=NR_ICE_CAND_STATE_INITIALIZING; + + switch(cand->type){ + case HOST: + if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr)) + ABORT(r); + cand->osock=cand->isock->sock; + // Post this so that it doesn't happen in-line + cand->ready_cb = ready_cb; + cand->ready_cb_arg = cb_arg; + NR_ASYNC_TIMER_SET(0, nr_ice_candidate_fire_ready_cb, (void *)cand, &cand->ready_cb_timer); + break; +#ifdef USE_TURN + case RELAYED: + protocol=NR_RESOLVE_PROTOCOL_TURN; + /* Fall through */ +#endif + case SERVER_REFLEXIVE: + if(cand->stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) { + if(nr_transport_addr_cmp(&cand->base,&cand->stun_server->u.addr,NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL)) { + r_log(LOG_ICE, LOG_INFO, "ICE-CANDIDATE(%s): Skipping srflx/relayed candidate because of IP version/transport mis-match with STUN/TURN server (%u/%s - %u/%s).", cand->label,cand->base.ip_version,cand->base.protocol==IPPROTO_UDP?"UDP":"TCP",cand->stun_server->u.addr.ip_version,cand->stun_server->u.addr.protocol==IPPROTO_UDP?"UDP":"TCP"); + ABORT(R_NOT_FOUND); /* Same error code when DNS lookup fails */ + } + + /* Just copy the address */ + if (r=nr_transport_addr_copy(&cand->stun_server_addr, + &cand->stun_server->u.addr)) { + r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not copy STUN server addr", cand->label); + ABORT(r); + } + + if(r=nr_ice_candidate_initialize2(cand)) + ABORT(r); + } + else { + nr_resolver_resource resource; + resource.domain_name=cand->stun_server->u.dnsname.host; + resource.port=cand->stun_server->u.dnsname.port; + resource.stun_turn=protocol; + resource.transport_protocol=cand->stun_server->transport; + + switch (cand->base.ip_version) { + case NR_IPV4: + resource.address_family=AF_INET; + break; + case NR_IPV6: + resource.address_family=AF_INET6; + break; + default: + ABORT(R_BAD_ARGS); + } + + /* Try to resolve */ + if(!cand->ctx->resolver) { + r_log(LOG_ICE, LOG_ERR, "ICE-CANDIDATE(%s): Can't use DNS names without a resolver", cand->label); + ABORT(R_BAD_ARGS); + } + + if(r=nr_resolver_resolve(cand->ctx->resolver, + &resource, + nr_ice_candidate_resolved_cb, + (void *)cand, + &cand->resolver_handle)){ + r_log(LOG_ICE,LOG_ERR,"ICE-CANDIDATE(%s): Could not invoke DNS resolver",cand->label); + ABORT(r); + } + } + break; + default: + ABORT(R_INTERNAL); + } + + nr_ice_candidate_compute_codeword(cand); + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + return(_status); + } + + +static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->resolver_handle=0; + + if(addr){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): resolved candidate %s. addr=%s", + cand->ctx->label,cand->label,addr->as_string); + } + else { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to resolve candidate %s.", + cand->ctx->label,cand->label); + ABORT(R_NOT_FOUND); + } + + /* Copy the address */ + if(r=nr_transport_addr_copy(&cand->stun_server_addr,addr)) + ABORT(r); + + if (cand->tcp_type == TCP_TYPE_PASSIVE || cand->tcp_type == TCP_TYPE_SO){ + if (r=nr_socket_multi_tcp_stun_server_connect(cand->osock, addr)) + ABORT(r); + } + + /* Now start initializing */ + if(r=nr_ice_candidate_initialize2(cand)) + ABORT(r); + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) { + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + return(_status); + } + +static int nr_ice_candidate_initialize2(nr_ice_candidate *cand) + { + int r,_status; + + switch(cand->type){ + case HOST: + assert(0); /* Can't happen */ + ABORT(R_INTERNAL); + break; +#ifdef USE_TURN + case RELAYED: + if(r=nr_ice_start_relay_turn(cand)) + ABORT(r); + ABORT(R_WOULDBLOCK); + break; +#endif /* USE_TURN */ + case SERVER_REFLEXIVE: + /* Need to start stun */ + if(r=nr_ice_srvrflx_start_stun(cand)) + ABORT(r); + cand->osock=cand->isock->sock; + ABORT(R_WOULDBLOCK); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + +/* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart + * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the + * TODO: server may not have understood the 0.96-style request */ + if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH, nr_ice_srvrflx_stun_finished_cb, cand)) + ABORT(r); + + if(r=nr_ice_ctx_remember_id(cand->ctx, cand->u.srvrflx.stun->request)) + ABORT(r); + + if(r=nr_ice_socket_register_stun_client(cand->isock,cand->u.srvrflx.stun,&cand->u.srvrflx.stun_handle)) + ABORT(r); + + _status=0; + abort: + if (_status && (cand->u.srvrflx.stun->state==NR_STUN_CLIENT_STATE_RUNNING)) { + nr_stun_client_failed(cand->u.srvrflx.stun); + } + return; + } + +static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand) + { + int r,_status; + + assert(!cand->delay_timer); + if(r=nr_stun_client_ctx_create(cand->label, cand->isock->sock, + &cand->stun_server_addr, cand->stream->ctx->gather_rto, + &cand->u.srvrflx.stun)) + ABORT(r); + + NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_srvrflx_start_stun_timer_cb,cand,&cand->delay_timer); + cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; + + _status=0; + abort: + return(_status); + } + +#ifdef USE_TURN +static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + + if(r=nr_turn_client_allocate(cand->u.relayed.turn, nr_ice_turn_allocated_cb, cb_arg)) + ABORT(r); + + if(r=nr_ice_socket_register_turn_client(cand->isock, cand->u.relayed.turn, + cand->osock, &cand->u.relayed.turn_handle)) + ABORT(r); + + _status=0; + abort: + if(_status && (cand->u.relayed.turn->state==NR_TURN_CLIENT_STATE_ALLOCATING)){ + nr_turn_client_failed(cand->u.relayed.turn); + } + return; + } + +static int nr_ice_start_relay_turn(nr_ice_candidate *cand) + { + int r,_status; + assert(!cand->delay_timer); + if(r=nr_turn_client_ctx_create(cand->label, cand->isock->sock, + cand->u.relayed.server->username, + cand->u.relayed.server->password, + &cand->stun_server_addr, + &cand->u.relayed.turn)) + ABORT(r); + + if(r=nr_socket_turn_set_ctx(cand->osock, cand->u.relayed.turn)) + ABORT(r); + + NR_ASYNC_TIMER_SET(cand->stream->ctx->stun_delay,nr_ice_start_relay_turn_timer_cb,cand,&cand->delay_timer); + cand->stream->ctx->stun_delay += cand->stream->ctx->Ta; + + _status=0; + abort: + return(_status); + } +#endif /* USE_TURN */ + +static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg) + { + int _status; + nr_ice_candidate *cand=cb_arg; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): %s",cand->ctx->label,cand->label,__FUNCTION__); + + /* Deregister to suppress duplicates */ + if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */ + nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle); + cand->u.srvrflx.stun_handle=0; + } + + switch(cand->u.srvrflx.stun->state){ + /* OK, we should have a mapped address */ + case NR_STUN_CLIENT_STATE_DONE: + /* Copy the address */ + nr_transport_addr_copy(&cand->addr, &cand->u.srvrflx.stun->results.stun_binding_response.mapped_addr); + cand->addr.protocol=cand->base.protocol; + nr_transport_addr_fmt_addr_string(&cand->addr); + nr_stun_client_ctx_destroy(&cand->u.srvrflx.stun); + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED); + cand=0; + break; + + /* This failed, so go to the next STUN server if there is one */ + case NR_STUN_CLIENT_STATE_FAILED: + ABORT(R_NOT_FOUND); + break; + default: + ABORT(R_INTERNAL); + } + _status = 0; + abort: + if(_status){ + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + } + +#ifdef USE_TURN +static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_candidate *cand=cb_arg; + nr_turn_client_ctx *turn=cand->u.relayed.turn; + char *label; + nr_transport_addr relay_addr; + + switch(turn->state){ + /* OK, we should have a mapped address */ + case NR_TURN_CLIENT_STATE_ALLOCATED: + if (r=nr_turn_client_get_relayed_address(turn, &relay_addr)) + ABORT(r); + + if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|", + relay_addr.as_string,")",NULL)) + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"TURN-CLIENT(%s)/CAND(%s): Switching from TURN to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label); + + /* Copy the relayed address into the candidate addr and + into the candidate base. Note that we need to keep the + ifname in the base. */ + if (r=nr_transport_addr_copy(&cand->addr, &relay_addr)) + ABORT(r); + if (r=nr_transport_addr_copy_keep_ifname(&cand->base, &relay_addr)) /* Need to keep interface for priority calculation */ + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): new relay base=%s addr=%s", cand->ctx->label, cand->label, cand->base.as_string, cand->addr.as_string); + + RFREE(cand->label); + cand->label=label; + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_INITIALIZED); + cand = 0; + + break; + + case NR_TURN_CLIENT_STATE_FAILED: + case NR_TURN_CLIENT_STATE_CANCELLED: + r_log(NR_LOG_TURN, LOG_WARNING, + "ICE-CANDIDATE(%s): nr_turn_allocated_cb called with state %d", + cand->label, turn->state); + /* This failed, so go to the next TURN server if there is one */ + ABORT(R_NOT_FOUND); + break; + default: + assert(0); /* should never happen */ + ABORT(R_INTERNAL); + } + + _status=0; + abort: + if(_status){ + if (cand) { + r_log(NR_LOG_TURN, LOG_WARNING, + "ICE-CANDIDATE(%s): nr_turn_allocated_cb failed", cand->label); + nr_ice_candidate_mark_done(cand, NR_ICE_CAND_STATE_FAILED); + } + } + } +#endif /* USE_TURN */ + +/* Format the candidate attribute as per ICE S 15.1 */ +int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen) + { + int r,_status; + char addr[64]; + int port; + int len; + nr_transport_addr *raddr; + + assert(!strcmp(nr_ice_candidate_type_names[HOST], "host")); + assert(!strcmp(nr_ice_candidate_type_names[RELAYED], "relay")); + + if(r=nr_transport_addr_get_addrstring(&cand->addr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(&cand->addr,&port)) + ABORT(r); + /* https://tools.ietf.org/html/rfc6544#section-4.5 */ + if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type==TCP_TYPE_ACTIVE) + port=9; + snprintf(attr,maxlen,"candidate:%s %d %s %u %s %d typ %s", + cand->foundation, cand->component_id, cand->addr.protocol==IPPROTO_UDP?"UDP":"TCP",cand->priority, addr, port, + nr_ctype_name(cand->type)); + + len=strlen(attr); attr+=len; maxlen-=len; + + /* raddr, rport */ + raddr = (cand->stream->ctx->flags & + (NR_ICE_CTX_FLAGS_RELAY_ONLY | + NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES)) ? + &cand->addr : &cand->base; + + switch(cand->type){ + case HOST: + break; + case SERVER_REFLEXIVE: + case PEER_REFLEXIVE: + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + break; + case RELAYED: + // comes from XorMappedAddress via AllocateResponse + if(r=nr_transport_addr_get_addrstring(raddr,addr,sizeof(addr))) + ABORT(r); + if(r=nr_transport_addr_get_port(raddr,&port)) + ABORT(r); + + snprintf(attr,maxlen," raddr %s rport %d",addr,port); + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + if (cand->base.protocol==IPPROTO_TCP && cand->tcp_type){ + len=strlen(attr); + attr+=len; + maxlen-=len; + snprintf(attr,maxlen," tcptype %s",nr_tcp_type_name(cand->tcp_type)); + } + + _status=0; + abort: + return(_status); + } + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h new file mode 100644 index 000000000..16be40276 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.h @@ -0,0 +1,121 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_candidate_h +#define _ice_candidate_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef enum {HOST=1, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED, CTYPE_MAX} nr_ice_candidate_type; + +struct nr_ice_candidate_ { + char *label; + char codeword[5]; + int state; +#define NR_ICE_CAND_STATE_CREATED 1 +#define NR_ICE_CAND_STATE_INITIALIZING 2 +#define NR_ICE_CAND_STATE_INITIALIZED 3 +#define NR_ICE_CAND_STATE_FAILED 4 +#define NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED 9 +#define NR_ICE_CAND_PEER_CANDIDATE_PAIRED 10 + struct nr_ice_ctx_ *ctx; + nr_ice_socket *isock; /* The socket to read from + (it contains all other candidates + on this socket) */ + nr_socket *osock; /* The socket to write to */ + nr_ice_media_stream *stream; /* The media stream this is associated with */ + nr_ice_component *component; /* The component this is associated with */ + nr_ice_candidate_type type; /* The type of the candidate (S 4.1.1) */ + nr_socket_tcp_type tcp_type; + UCHAR component_id; /* The component id (S 4.1.2.1) */ + nr_transport_addr addr; /* The advertised address; + JDR calls this the candidate */ + nr_transport_addr base; /* The base address (S 2.1)*/ + char *foundation; /* Foundation for the candidate (S 4) */ + UINT4 priority; /* The priority value (S 5.4 */ + nr_ice_stun_server *stun_server; + nr_transport_addr stun_server_addr; /* Resolved STUN server address */ + void *delay_timer; + void *resolver_handle; + + /* Holding data for STUN and TURN */ + union { + struct { + nr_stun_client_ctx *stun; + void *stun_handle; + /* If this is a srflx that is piggybacking on a relay candidate, this is + * a back pointer to that relay candidate. */ + nr_ice_candidate *relay_candidate; + } srvrflx; + struct { + nr_turn_client_ctx *turn; + nr_ice_turn_server *server; + nr_ice_candidate *srvflx_candidate; + nr_socket *turn_sock; + void *turn_handle; + } relayed; + } u; + + NR_async_cb done_cb; + void *cb_arg; + + NR_async_cb ready_cb; + void *ready_cb_arg; + void *ready_cb_timer; + + TAILQ_ENTRY(nr_ice_candidate_) entry_sock; + TAILQ_ENTRY(nr_ice_candidate_) entry_comp; +}; + +extern char *nr_ice_candidate_type_names[]; +extern char *nr_ice_candidate_tcp_type_names[]; + + +int nr_ice_candidate_create(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_socket_tcp_type tcp_type, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp); +int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); +void nr_ice_candidate_compute_codeword(nr_ice_candidate *cand); +int nr_ice_candidate_process_stun(nr_ice_candidate *cand, UCHAR *msg, int len, nr_transport_addr *faddr); +int nr_ice_candidate_destroy(nr_ice_candidate **candp); +int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen); +int nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *attr,nr_ice_media_stream *stream,nr_ice_candidate **candp); +int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp); +int nr_ice_candidate_compute_priority(nr_ice_candidate *cand); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c new file mode 100644 index 000000000..6c07ae2a6 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.c @@ -0,0 +1,684 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_candidate_pair.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <assert.h> +#include <string.h> +#include <nr_api.h> +#include "async_timer.h" +#include "ice_ctx.h" +#include "ice_util.h" +#include "ice_codeword.h" +#include "stun.h" + +static char *nr_ice_cand_pair_states[]={"UNKNOWN","FROZEN","WAITING","IN_PROGRESS","FAILED","SUCCEEDED","CANCELLED"}; + +static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg); +static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand); + +static void nr_ice_candidate_pair_set_priority(nr_ice_cand_pair *pair) + { + /* Priority computation S 5.7.2 */ + UINT8 controlling_priority, controlled_priority; + if(pair->pctx->controlling) + { + controlling_priority=pair->local->priority; + controlled_priority=pair->remote->priority; + } + else{ + controlling_priority=pair->remote->priority; + controlled_priority=pair->local->priority; + } + pair->priority=(MIN(controlling_priority, controlled_priority))<<32 | + (MAX(controlling_priority, controlled_priority))<<1 | + (controlled_priority > controlling_priority?0:1); + } + +int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp) + { + nr_ice_cand_pair *pair=0; + int r,_status; + UINT4 RTO; + nr_ice_candidate tmpcand; + UINT8 t_priority; + + if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair)))) + ABORT(R_NO_MEMORY); + + pair->pctx=pctx; + + nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand); + + if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|", + rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL)) + ABORT(r); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + pair->local=lcand; + pair->remote=rcand; + + nr_ice_candidate_pair_set_priority(pair); + + /* + TODO(bcampen@mozilla.com): Would be nice to log why this candidate was + created (initial pair generation, triggered check, and new trickle + candidate seem to be the possibilities here). + */ + r_log(LOG_ICE,LOG_INFO,"ICE(%s)/CAND-PAIR(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx)",pctx->ctx->label,pair->codeword,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority); + + /* Foundation */ + if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|", + rcand->foundation,NULL)) + ABORT(r); + + /* Compute the RTO per S 16 */ + RTO = MAX(100, (pctx->ctx->Ta * pctx->waiting_pairs)); + + /* Make a bogus candidate to compute a theoretical peer reflexive + * priority per S 7.1.1.1 */ + memcpy(&tmpcand, lcand, sizeof(tmpcand)); + tmpcand.type = PEER_REFLEXIVE; + if (r=nr_ice_candidate_compute_priority(&tmpcand)) + ABORT(r); + t_priority = tmpcand.priority; + + /* Our sending context */ + if(r=nr_stun_client_ctx_create(pair->as_string, + lcand->osock, + &rcand->addr,RTO,&pair->stun_client)) + ABORT(r); + if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(rcand->stream->l2r_user))) + ABORT(R_NO_MEMORY); + if(r=r_data_copy(&pair->stun_client->params.ice_binding_request.password, + &rcand->stream->l2r_pass)) + ABORT(r); + /* TODO(ekr@rtfm.com): Do we need to frob this when we change role. Bug 890667 */ + pair->stun_client->params.ice_binding_request.control = pctx->controlling? + NR_ICE_CONTROLLING:NR_ICE_CONTROLLED; + pair->stun_client->params.ice_binding_request.priority=t_priority; + + pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker; + + *pairp=pair; + + _status=0; + abort: + if(_status){ + nr_ice_candidate_pair_destroy(&pair); + } + return(_status); + } + +int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp) + { + nr_ice_cand_pair *pair; + + if(!pairp || !*pairp) + return(0); + + pair=*pairp; + *pairp=0; + + RFREE(pair->as_string); + RFREE(pair->foundation); + nr_ice_socket_deregister(pair->local->isock,pair->stun_client_handle); + if (pair->stun_client) { + RFREE(pair->stun_client->params.ice_binding_request.username); + RFREE(pair->stun_client->params.ice_binding_request.password.data); + nr_stun_client_ctx_destroy(&pair->stun_client); + } + + NR_async_timer_cancel(pair->stun_cb_timer); + NR_async_timer_cancel(pair->restart_role_change_cb_timer); + NR_async_timer_cancel(pair->restart_nominated_cb_timer); + + RFREE(pair); + return(0); + } + +int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + assert(pair->state==NR_ICE_PAIR_STATE_FROZEN); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING); + + return(0); + } + +static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_cand_pair *pair=cb_arg; + nr_ice_cand_pair *actual_pair=0; + nr_ice_candidate *cand=0; + nr_stun_message *sres; + nr_transport_addr *request_src; + nr_transport_addr *request_dst; + nr_transport_addr *response_src; + nr_transport_addr response_dst; + nr_stun_message_attribute *attr; + + pair->stun_cb_timer=0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s): STUN cb on pair addr = %s", + pair->pctx->label,pair->local->stream->label,pair->codeword,pair->as_string); + + /* This ordinarily shouldn't happen, but can if we're + doing the second check to confirm nomination. + Just bail out */ + if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) + goto done; + + switch(pair->stun_client->state){ + case NR_STUN_CLIENT_STATE_FAILED: + sres=pair->stun_client->response; + if(sres && nr_stun_message_has_attribute(sres,NR_STUN_ATTR_ERROR_CODE,&attr)&&attr->u.error_code.number==487){ + + /* + * Flip the controlling bit; subsequent 487s for other pairs will be + * ignored, since we abandon their STUN transactions. + */ + nr_ice_peer_ctx_switch_controlling_role(pair->pctx); + + return; + } + /* Fall through */ + case NR_STUN_CLIENT_STATE_TIMED_OUT: + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + case NR_STUN_CLIENT_STATE_DONE: + /* make sure the addresses match up S 7.1.2.2 */ + response_src=&pair->stun_client->peer_addr; + request_dst=&pair->remote->addr; + if (nr_transport_addr_cmp(response_src,request_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Peer address mismatch %s != %s",pair->pctx->label,pair->codeword,response_src->as_string,request_dst->as_string); + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + } + request_src=&pair->stun_client->my_addr; + nr_socket_getaddr(pair->local->osock,&response_dst); + if (nr_transport_addr_cmp(request_src,&response_dst,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Local address mismatch %s != %s",pair->pctx->label,pair->codeword,request_src->as_string,response_dst.as_string); + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + } + + if(strlen(pair->stun_client->results.ice_binding_response.mapped_addr.as_string)==0){ + /* we're using the mapped_addr returned by the server to lookup our + * candidate, but if the server fails to do that we can't perform + * the lookup -- this may be a BUG because if we've gotten here + * then the transaction ID check succeeded, and perhaps we should + * just assume that it's the server we're talking to and that our + * peer is ok, but I'm not sure how that'll interact with the + * peer reflexive logic below */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): server failed to return mapped address on pair %s", pair->pctx->label,pair->codeword,pair->as_string); + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + break; + } + else if(!nr_transport_addr_cmp(&pair->local->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_SUCCEEDED); + } + else if(pair->stun_client->state == NR_STUN_CLIENT_STATE_DONE) { + /* OK, this didn't correspond to a pair on the check list, but + it probably matches one of our candidates */ + + cand=TAILQ_FIRST(&pair->local->component->candidates); + while(cand){ + if(!nr_transport_addr_cmp(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): found pre-existing local candidate of type %d for mapped address %s", pair->pctx->label,cand->type,cand->addr.as_string); + assert(cand->type != HOST); + break; + } + + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!cand) { + /* OK, nothing found, must be a new peer reflexive */ + if (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) { + /* Any STUN response with a reflexive address in it is unwanted + when we'll send on relay only. Bail since cand is used below. */ + goto done; + } + if(r=nr_ice_candidate_create(pair->pctx->ctx, + pair->local->component,pair->local->isock,pair->local->osock, + PEER_REFLEXIVE,pair->local->tcp_type,0,pair->local->component->component_id,&cand)) + ABORT(r); + if(r=nr_transport_addr_copy(&cand->addr,&pair->stun_client->results.ice_binding_response.mapped_addr)) + ABORT(r); + cand->state=NR_ICE_CAND_STATE_INITIALIZED; + TAILQ_INSERT_TAIL(&pair->local->component->candidates,cand,entry_comp); + } else { + /* Check if we have a pair for this candidate already. */ + if(r=nr_ice_media_stream_find_pair(pair->remote->stream, cand, pair->remote, &actual_pair)) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no pair exists for %s and %s", pair->pctx->label,cand->addr.as_string, pair->remote->addr.as_string); + } + } + + if(!actual_pair) { + if(r=nr_ice_candidate_pair_create(pair->pctx,cand,pair->remote, &actual_pair)) + ABORT(r); + + if(r=nr_ice_component_insert_pair(actual_pair->remote->component,actual_pair)) + ABORT(r); + + /* If the original pair was nominated, make us nominated too. */ + if(pair->peer_nominated) + actual_pair->peer_nominated=1; + + /* Now mark the orig pair failed */ + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FAILED); + } + + assert(actual_pair); + nr_ice_candidate_pair_set_state(actual_pair->pctx,actual_pair,NR_ICE_PAIR_STATE_SUCCEEDED); + pair=actual_pair; + + } + + /* Should we set nominated? */ + if(pair->pctx->controlling){ + if(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION) + pair->nominated=1; + } + else{ + if(pair->peer_nominated) + pair->nominated=1; + } + + + /* increment the number of valid pairs in the component */ + /* We don't bother to maintain a separate valid list */ + pair->remote->component->valid_pairs++; + + /* S 7.1.2.2: unfreeze other pairs with the same foundation*/ + if(r=nr_ice_media_stream_unfreeze_pairs_foundation(pair->remote->stream,pair->foundation)) + ABORT(r); + + /* Deal with this pair being nominated */ + if(pair->nominated){ + if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) + ABORT(r); + } + + break; + default: + ABORT(R_INTERNAL); + } + + /* If we're controlling but in regular mode, ask the handler + if he wants to nominate something and stop... */ + if(pair->pctx->controlling && !(pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)){ + + if(r=nr_ice_component_select_pair(pair->pctx,pair->remote->component)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + } + + done: + _status=0; + abort: + return; + } + +static void nr_ice_candidate_pair_restart(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + int r,_status; + UINT4 mode; + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_IN_PROGRESS); + + /* Start STUN */ + if(pair->pctx->controlling && (pair->pctx->ctx->flags & NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION)) + mode=NR_ICE_CLIENT_MODE_USE_CANDIDATE; + else + mode=NR_ICE_CLIENT_MODE_BINDING_REQUEST; + + nr_stun_client_reset(pair->stun_client); + + if(r=nr_stun_client_start(pair->stun_client,mode,nr_ice_candidate_pair_stun_cb,pair)) + ABORT(r); + + if ((r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request))) { + /* ignore if this fails (which it shouldn't) because it's only an + * optimization and the cleanup routines are not going to do the right + * thing if this fails */ + assert(0); + } + + _status=0; + abort: + if(_status){ + /* Don't fire the CB, but schedule it to fire ASAP */ + assert(!pair->stun_cb_timer); + NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_stun_cb,pair, &pair->stun_cb_timer); + _status=0; + } + } + +int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + int r,_status; + + /* Register the stun ctx for when responses come in*/ + if(r=nr_ice_socket_register_stun_client(pair->local->isock,pair->stun_client,&pair->stun_client_handle)) + ABORT(r); + + nr_ice_candidate_pair_restart(pctx, pair); + + _status=0; + abort: + return(_status); + } + +static int nr_ice_candidate_copy_for_triggered_check(nr_ice_cand_pair *pair) + { + int r,_status; + nr_ice_cand_pair *copy; + + if(r=nr_ice_candidate_pair_create(pair->pctx, pair->local, pair->remote, ©)) + ABORT(r); + + /* Preserve nomination status */ + copy->peer_nominated= pair->peer_nominated; + copy->nominated = pair->nominated; + + r_log(LOG_ICE,LOG_INFO,"CAND-PAIR(%s): Adding pair to check list and trigger check queue: %s",pair->codeword,pair->as_string); + if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,copy)) + ABORT(r); + nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,copy); + + copy->triggered = 1; + nr_ice_candidate_pair_set_state(copy->pctx,copy,NR_ICE_PAIR_STATE_WAITING); + + _status=0; + abort: + return(_status); +} + +int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + int r,_status; + + if(pair->state==NR_ICE_PAIR_STATE_CANCELLED) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Ignoring matching but canceled pair",pctx->label,pair->codeword); + return(0); + } else if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): No new trigger check for succeeded pair",pctx->label,pair->codeword); + return(0); + } + + /* Do not run this logic more than once on a given pair */ + if(!pair->triggered){ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): triggered check on %s",pctx->label,pair->codeword,pair->as_string); + + pair->triggered=1; + + switch(pair->state){ + case NR_ICE_PAIR_STATE_FAILED: + /* OK, there was a pair, it's just invalid: According to Section + * 7.2.1.4, we need to resurrect it */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): received STUN check on failed pair, resurrecting: %s",pctx->label,pair->codeword,pair->as_string); + /* fall through */ + case NR_ICE_PAIR_STATE_FROZEN: + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING); + /* fall through even further */ + case NR_ICE_PAIR_STATE_WAITING: + /* Append it additionally to the trigger check queue */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): Inserting pair to trigger check queue: %s",pctx->label,pair->codeword,pair->as_string); + nr_ice_candidate_pair_trigger_check_append(&pair->remote->stream->trigger_check_queue,pair); + break; + case NR_ICE_PAIR_STATE_IN_PROGRESS: + /* Instead of trying to maintain two stun contexts on the same pair, + * and handling heterogenous responses and error conditions, we instead + * create a second pair that is identical except that it has the + * |triggered| bit set. We also cancel the original pair, but it can + * still succeed on its own in the special waiting state. */ + if(r=nr_ice_candidate_copy_for_triggered_check(pair)) + ABORT(r); + nr_ice_candidate_pair_cancel(pair->pctx,pair,1); + break; + default: + /* all states are handled - a new/unknown state should not + * automatically enter the start_checks() below */ + assert(0); + break; + } + + /* Ensure that the timers are running to start checks on the topmost entry + * of the triggered check queue. */ + if(r=nr_ice_media_stream_start_checks(pair->pctx,pair->remote->stream)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state) + { + if(pair->state != NR_ICE_PAIR_STATE_FAILED){ + /* If it's already running we need to terminate the stun */ + if(pair->state==NR_ICE_PAIR_STATE_IN_PROGRESS){ + if(move_to_wait_state) { + nr_stun_client_wait(pair->stun_client); + } else { + nr_stun_client_cancel(pair->stun_client); + } + } + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_CANCELLED); + } + + return(0); + } + +int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair) + { + int r,_status; + + if(!pair){ + r_log(LOG_ICE,LOG_ERR,"ICE-PAIR: No pair chosen"); + ABORT(R_BAD_ARGS); + } + + if(pair->state!=NR_ICE_PAIR_STATE_SUCCEEDED){ + r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s)/CAND-PAIR(%s): tried to install non-succeeded pair, ignoring: %s",pair->pctx->label,pair->codeword,pair->as_string); + } + else{ + /* Ok, they chose one */ + /* 1. Send a new request with nominated. Do it as a scheduled + event to avoid reentrancy issues. Only do this if it hasn't + happened already (though this shouldn't happen.) + */ + if(!pair->restart_nominated_cb_timer) + NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_nominated_cb,pair,&pair->restart_nominated_cb_timer); + + /* 2. Tell ourselves this pair is ready */ + if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state) + { + int r,_status; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/CAND-PAIR(%s): setting pair to state %s: %s", + pctx->label,pair->codeword,nr_ice_cand_pair_states[state],pair->as_string); + + /* NOTE: This function used to reference pctx->state instead of + pair->state and the assignment to pair->state was at the top + of this function. Because pctx->state was never changed, this seems to have + been a typo. The natural logic is "if the state changed + decrement the counter" so this implies we should be checking + the pair state rather than the pctx->state. + + This didn't cause big problems because waiting_pairs was only + used for pacing, so the pacing just was kind of broken. + + This note is here as a reminder until we do more testing + and make sure that in fact this was a typo. + */ + if(pair->state!=NR_ICE_PAIR_STATE_WAITING){ + if(state==NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs++; + } + else{ + if(state!=NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs--; + + assert(pctx->waiting_pairs>=0); + } + pair->state=state; + + + if(pair->state==NR_ICE_PAIR_STATE_FAILED || + pair->state==NR_ICE_PAIR_STATE_CANCELLED){ + if(r=nr_ice_component_failed_pair(pair->remote->component, pair)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out) + { + /*r_log(LOG_ICE,LOG_DEBUG,"CAND-PAIR(%s): pair %s: state=%s, priority=0x%llx\n",pair->codeword,pair->as_string,nr_ice_cand_pair_states[pair->state],pair->priority);*/ + + return(0); + } + + +int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair) + { + if(pair->triggered_check_queue_entry.tqe_next || + pair->triggered_check_queue_entry.tqe_prev) + return(0); + + TAILQ_INSERT_TAIL(head,pair,triggered_check_queue_entry); + + return(0); + } + +int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair) + { + nr_ice_cand_pair *c1; + + c1=TAILQ_FIRST(head); + while(c1){ + if(c1->priority < pair->priority){ + TAILQ_INSERT_BEFORE(c1,pair,check_queue_entry); + break; + } + + c1=TAILQ_NEXT(c1,check_queue_entry); + } + if(!c1) TAILQ_INSERT_TAIL(head,pair,check_queue_entry); + + return(0); + } + +void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + int r,_status; + + pair->restart_nominated_cb_timer=0; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s)/COMP(%d): Restarting pair as nominated: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->as_string); + + nr_stun_client_reset(pair->stun_client); + + if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair)) + ABORT(r); + + if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request)) + ABORT(r); + + _status=0; + abort: + return; + } + +static void nr_ice_candidate_pair_restart_stun_role_change_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + + pair->restart_role_change_cb_timer=0; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/CAND-PAIR(%s):COMP(%d): Restarting pair as %s: %s",pair->pctx->label,pair->local->stream->label,pair->codeword,pair->remote->component->component_id,pair->pctx->controlling ? "CONTROLLING" : "CONTROLLED",pair->as_string); + + nr_ice_candidate_pair_restart(pair->pctx, pair); + } + +void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair) + { + pair->stun_client->params.ice_binding_request.control = pair->pctx->controlling ? NR_ICE_CONTROLLING : NR_ICE_CONTROLLED; + nr_ice_candidate_pair_set_priority(pair); + + if(pair->state == NR_ICE_PAIR_STATE_IN_PROGRESS) { + /* We could try only restarting in-progress pairs when they receive their + * 487, but this ends up being simpler, because any extra 487 are dropped. + */ + if(!pair->restart_role_change_cb_timer) + NR_ASYNC_TIMER_SET(0,nr_ice_candidate_pair_restart_stun_role_change_cb,pair,&pair->restart_role_change_cb_timer); + } + } + +static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand) + { + char as_string[2048]; + + snprintf(as_string, + sizeof(as_string), + "%s|%s(%s|%s)", + lcand->addr.as_string, + rcand->addr.as_string, + lcand->label, + rcand->label); + + nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword); + } + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h new file mode 100644 index 000000000..171ded4a0 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate_pair.h @@ -0,0 +1,95 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_candidate_pair_h +#define _ice_candidate_pair_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + + +struct nr_ice_cand_pair_ { + nr_ice_peer_ctx *pctx; + char codeword[5]; + char *as_string; + int state; /* The current state (S 5.7.3) */ +#define NR_ICE_PAIR_STATE_FROZEN 1 +#define NR_ICE_PAIR_STATE_WAITING 2 +#define NR_ICE_PAIR_STATE_IN_PROGRESS 3 +#define NR_ICE_PAIR_STATE_FAILED 4 +#define NR_ICE_PAIR_STATE_SUCCEEDED 5 +#define NR_ICE_PAIR_STATE_CANCELLED 6 + + UCHAR peer_nominated; /* The peer sent USE-CANDIDATE + on this check */ + UCHAR nominated; /* Is this nominated or not */ + + UCHAR triggered; /* Ignore further trigger check requests */ + + UINT8 priority; /* The priority for this pair */ + nr_ice_candidate *local; /* The local candidate */ + nr_ice_candidate *remote; /* The remote candidate */ + char *foundation; /* The combined foundations */ + + nr_stun_client_ctx *stun_client; /* STUN context when acting as a client */ + void *stun_client_handle; + + void *stun_cb_timer; + void *restart_role_change_cb_timer; + void *restart_nominated_cb_timer; + + TAILQ_ENTRY(nr_ice_cand_pair_) check_queue_entry; /* the check list */ + TAILQ_ENTRY(nr_ice_cand_pair_) triggered_check_queue_entry; /* the trigger check queue */ +}; + +int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp); +int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state); +int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out); +int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair, int move_to_wait_state); +int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair); +int nr_ice_candidate_pair_trigger_check_append(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair); +void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg); +int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp); +void nr_ice_candidate_pair_role_change(nr_ice_cand_pair *pair); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_codeword.h b/media/mtransport/third_party/nICEr/src/ice/ice_codeword.h new file mode 100644 index 000000000..76fce0b69 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_codeword.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_codeword_h +#define _ice_codeword_h + +void nr_ice_compute_codeword(char *buf, int len,char *codeword); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_component.c b/media/mtransport/third_party/nICEr/src/ice/ice_component.c new file mode 100644 index 000000000..2be25efca --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c @@ -0,0 +1,1688 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_component.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <string.h> +#include <assert.h> +#include <nr_api.h> +#include <registry.h> +#include <async_timer.h> +#include "ice_ctx.h" +#include "ice_codeword.h" +#include "stun.h" +#include "nr_socket_local.h" +#include "nr_socket_turn.h" +#include "nr_socket_wrapper.h" +#include "nr_socket_buffered_stun.h" +#include "nr_socket_multi_tcp.h" +#include "ice_reg.h" +#include "nr_crypto.h" +#include "r_time.h" + +static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error); +static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp); +void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp); +void nr_ice_component_consent_destroy(nr_ice_component *comp); + +/* This function takes ownership of the contents of req (but not req itself) */ +static int nr_ice_pre_answer_request_create(nr_transport_addr *dst, nr_stun_server_request *req, nr_ice_pre_answer_request **parp) + { + int r, _status; + nr_ice_pre_answer_request *par = 0; + nr_stun_message_attribute *attr; + + if (!(par = RCALLOC(sizeof(nr_ice_pre_answer_request)))) + ABORT(R_NO_MEMORY); + + par->req = *req; /* Struct assignment */ + memset(req, 0, sizeof(*req)); /* Zero contents to avoid confusion */ + + if (r=nr_transport_addr_copy(&par->local_addr, dst)) + ABORT(r); + if (!nr_stun_message_has_attribute(par->req.request, NR_STUN_ATTR_USERNAME, &attr)) + ABORT(R_INTERNAL); + if (!(par->username = r_strdup(attr->u.username))) + ABORT(R_NO_MEMORY); + + *parp=par; + _status=0; + abort: + if (_status) { + /* Erase the request so we don't free it */ + memset(&par->req, 0, sizeof(nr_stun_server_request)); + nr_ice_pre_answer_request_destroy(&par); + } + + return(_status); + } + +static int nr_ice_pre_answer_request_destroy(nr_ice_pre_answer_request **parp) + { + nr_ice_pre_answer_request *par; + + if (!parp || !*parp) + return(0); + + par = *parp; + *parp = 0; + + nr_stun_message_destroy(&par->req.request); + nr_stun_message_destroy(&par->req.response); + + RFREE(par->username); + RFREE(par); + + return(0); + } + +int nr_ice_component_create(nr_ice_media_stream *stream, int component_id, nr_ice_component **componentp) + { + int _status; + nr_ice_component *comp=0; + + if(!(comp=RCALLOC(sizeof(nr_ice_component)))) + ABORT(R_NO_MEMORY); + + comp->state=NR_ICE_COMPONENT_UNPAIRED; + comp->component_id=component_id; + comp->stream=stream; + comp->ctx=stream->ctx; + + STAILQ_INIT(&comp->sockets); + TAILQ_INIT(&comp->candidates); + STAILQ_INIT(&comp->pre_answer_reqs); + + STAILQ_INSERT_TAIL(&stream->components,comp,entry); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_destroy(nr_ice_component **componentp) + { + nr_ice_component *component; + nr_ice_socket *s1,*s2; + nr_ice_candidate *c1,*c2; + nr_ice_pre_answer_request *r1,*r2; + + if(!componentp || !*componentp) + return(0); + + component=*componentp; + *componentp=0; + + nr_ice_component_consent_destroy(component); + + /* Detach ourselves from the sockets */ + if (component->local_component){ + nr_ice_socket *isock=STAILQ_FIRST(&component->local_component->sockets); + while(isock){ + nr_stun_server_remove_client(isock->stun_server, component); + isock=STAILQ_NEXT(isock, entry); + } + } + + /* candidates MUST be destroyed before the sockets so that + they can deregister */ + TAILQ_FOREACH_SAFE(c1, &component->candidates, entry_comp, c2){ + TAILQ_REMOVE(&component->candidates,c1,entry_comp); + nr_ice_candidate_destroy(&c1); + } + + STAILQ_FOREACH_SAFE(s1, &component->sockets, entry, s2){ + STAILQ_REMOVE(&component->sockets,s1,nr_ice_socket_,entry); + nr_ice_socket_destroy(&s1); + } + + STAILQ_FOREACH_SAFE(r1, &component->pre_answer_reqs, entry, r2){ + STAILQ_REMOVE(&component->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); + nr_ice_pre_answer_request_destroy(&r1); + } + + RFREE(component); + return(0); + } + +static int nr_ice_component_create_stun_server_ctx(nr_ice_component *component, nr_ice_socket *isock, nr_socket *sock, nr_transport_addr *addr, char *lufrag, Data *pwd) + { + char label[256]; + int r,_status; + + /* Create a STUN server context for this socket */ + snprintf(label, sizeof(label), "server(%s)", addr->as_string); + if(r=nr_stun_server_ctx_create(label,sock,&isock->stun_server)) + ABORT(r); + if(r=nr_ice_socket_register_stun_server(isock,isock->stun_server,&isock->stun_server_handle)) + ABORT(r); + + /* Add the default STUN credentials so that we can respond before + we hear about the peer.*/ + if(r=nr_stun_server_add_default_client(isock->stun_server, lufrag, pwd, nr_ice_component_stun_server_default_cb, component)) + ABORT(r); + + _status = 0; + abort: + return(_status); + } + +static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd) + { + nr_socket *sock; + nr_ice_socket *isock=0; + nr_ice_candidate *cand=0; + int i; + int j; + int r,_status; + + if(ctx->flags & NR_ICE_CTX_FLAGS_ONLY_PROXY) { + /* No UDP support if we must use a proxy */ + return 0; + } + + /* Now one ice_socket for each address */ + for(i=0;i<addr_ct;i++){ + char suppress; + + if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + else{ + if(suppress) + continue; + } + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): host address %s",ctx->label,addrs[i].addr.as_string); + if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addrs[i].addr,&sock))){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): couldn't create socket for address %s",ctx->label,addrs[i].addr.as_string); + continue; + } + + if(r=nr_ice_socket_create(ctx,component,sock,NR_ICE_SOCKET_TYPE_DGRAM,&isock)) + ABORT(r); + + if (!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) { + /* Create one host candidate */ + if(r=nr_ice_candidate_create(ctx,component,isock,sock,HOST,0,0, + component->component_id,&cand)) + ABORT(r); + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + + /* And a srvrflx candidate for each STUN server */ + for(j=0;j<ctx->stun_server_ct;j++){ + /* Skip non-UDP */ + if(ctx->stun_servers[j].transport!=IPPROTO_UDP) + continue; + + /* Ensure id is set (nr_ice_ctx_set_stun_servers does not) */ + ctx->stun_servers[j].id = j; + if(r=nr_ice_candidate_create(ctx,component, + isock,sock,SERVER_REFLEXIVE,0, + &ctx->stun_servers[j],component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + } + else{ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): relay only option results in no host candidate for %s",ctx->label,addrs[i].addr.as_string); + } + +#ifdef USE_TURN + if ((ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) && + (ctx->turn_server_ct == 0)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): relay only option is set without any TURN server configured",ctx->label); + } + /* And both a srvrflx and relayed candidate for each TURN server (unless + we're in relay-only mode, in which case just the relayed one) */ + for(j=0;j<ctx->turn_server_ct;j++){ + nr_socket *turn_sock; + nr_ice_candidate *srvflx_cand=0; + + /* Skip non-UDP */ + if (ctx->turn_servers[j].turn_server.transport != IPPROTO_UDP) + continue; + + if (!(ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY)) { + /* Ensure id is set with a unique value */ + ctx->turn_servers[j].turn_server.id = j + ctx->stun_server_ct; + /* srvrflx */ + if(r=nr_ice_candidate_create(ctx,component, + isock,sock,SERVER_REFLEXIVE,0, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + cand->state=NR_ICE_CAND_STATE_INITIALIZING; /* Don't start */ + cand->done_cb=nr_ice_gather_finished_cb; + cand->cb_arg=cand; + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + srvflx_cand=cand; + cand=0; + } + /* relayed*/ + if(r=nr_socket_turn_create(sock, &turn_sock)) + ABORT(r); + if(r=nr_ice_candidate_create(ctx,component, + isock,turn_sock,RELAYED,0, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + if (srvflx_cand) { + cand->u.relayed.srvflx_candidate=srvflx_cand; + srvflx_cand->u.srvrflx.relay_candidate=cand; + } + cand->u.relayed.server=&ctx->turn_servers[j]; + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + + cand=0; + } +#endif /* USE_TURN */ + + /* Create a STUN server context for this socket */ + if ((r=nr_ice_component_create_stun_server_ctx(component,isock,sock,&addrs[i].addr,lufrag,pwd))) + ABORT(r); + + STAILQ_INSERT_TAIL(&component->sockets,isock,entry); + } + + _status = 0; + abort: + return(_status); + } + +static int nr_ice_component_get_port_from_ephemeral_range(uint16_t *port) + { + int _status, r; + void *buf = port; + if(r=nr_crypto_random_bytes(buf, 2)) + ABORT(r); + *port|=49152; /* make it fit into IANA ephemeral port range >= 49152 */ + _status=0; +abort: + return(_status); + } + +static int nr_ice_component_create_tcp_host_candidate(struct nr_ice_ctx_ *ctx, + nr_ice_component *component, nr_transport_addr *interface_addr, nr_socket_tcp_type tcp_type, + int backlog, int so_sock_ct, char *lufrag, Data *pwd, nr_ice_socket **isock) + { + int r,_status; + nr_ice_candidate *cand=0; + int tries=3; + nr_ice_socket *isock_tmp=0; + nr_socket *nrsock=0; + nr_transport_addr addr; + uint16_t local_port; + + if ((r=nr_transport_addr_copy(&addr,interface_addr))) + ABORT(r); + addr.protocol=IPPROTO_TCP; + + do{ + if (!tries--) + ABORT(r); + + if((r=nr_ice_component_get_port_from_ephemeral_range(&local_port))) + ABORT(r); + + if ((r=nr_transport_addr_set_port(&addr, local_port))) + ABORT(r); + + if((r=nr_transport_addr_fmt_addr_string(&addr))) + ABORT(r); + + /* It would be better to stop trying if there is error other than + port already used, but it'd require significant work to support this. */ + r=nr_socket_multi_tcp_create(ctx,&addr,tcp_type,so_sock_ct,NR_STUN_MAX_MESSAGE_SIZE,&nrsock); + + } while(r); + + if((tcp_type == TCP_TYPE_PASSIVE) && (r=nr_socket_listen(nrsock,backlog))) + ABORT(r); + + if((r=nr_ice_socket_create(ctx,component,nrsock,NR_ICE_SOCKET_TYPE_STREAM_TCP,&isock_tmp))) + ABORT(r); + + /* nr_ice_socket took ownership of nrsock */ + nrsock=NULL; + + /* Create a STUN server context for this socket */ + if ((r=nr_ice_component_create_stun_server_ctx(component,isock_tmp,isock_tmp->sock,&addr,lufrag,pwd))) + ABORT(r); + + if((r=nr_ice_candidate_create(ctx,component,isock_tmp,isock_tmp->sock,HOST,tcp_type,0, + component->component_id,&cand))) + ABORT(r); + + if (isock) + *isock=isock_tmp; + + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + + STAILQ_INSERT_TAIL(&component->sockets,isock_tmp,entry); + + _status=0; +abort: + if (_status) { + nr_ice_socket_destroy(&isock_tmp); + nr_socket_destroy(&nrsock); + } + return(_status); + } + +static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_component *component, nr_local_addr *addrs, int addr_ct, char *lufrag, Data *pwd) + { + nr_ice_candidate *cand=0; + int i; + int j; + int r,_status; + int so_sock_ct=0; + int backlog=10; + char ice_tcp_disabled=1; + + r_log(LOG_ICE,LOG_DEBUG,"nr_ice_component_initialize_tcp"); + + if(r=NR_reg_get_int4(NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT,&so_sock_ct)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + if(r=NR_reg_get_int4(NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG,&backlog)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + if ((r=NR_reg_get_char(NR_ICE_REG_ICE_TCP_DISABLE, &ice_tcp_disabled))) { + if (r != R_NOT_FOUND) + ABORT(r); + } + if ((ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) || + (ctx->flags & NR_ICE_CTX_FLAGS_ONLY_PROXY)) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): relay/proxy only option results in ICE TCP being disabled",ctx->label); + ice_tcp_disabled = 1; + } + + for(i=0;i<addr_ct;i++){ + char suppress; + nr_ice_socket *isock_psv=0; + nr_ice_socket *isock_so=0; + + if(r=NR_reg_get2_char(NR_ICE_REG_SUPPRESS_INTERFACE_PRFX,addrs[i].addr.ifname,&suppress)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + else if(suppress) { + continue; + } + + if (!ice_tcp_disabled) { + /* passive host candidate */ + if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr, + TCP_TYPE_PASSIVE, backlog, 0, lufrag, pwd, &isock_psv))) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to create passive TCP host candidate: %d",ctx->label,r); + } + + /* active host candidate */ + if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr, + TCP_TYPE_ACTIVE, 0, 0, lufrag, pwd, NULL))) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to create active TCP host candidate: %d",ctx->label,r); + } + + /* simultaneous-open host candidate */ + if (so_sock_ct) { + if ((r=nr_ice_component_create_tcp_host_candidate(ctx, component, &addrs[i].addr, + TCP_TYPE_SO, 0, so_sock_ct, lufrag, pwd, &isock_so))) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): failed to create simultanous open TCP host candidate: %d",ctx->label,r); + } + } + + /* And srvrflx candidates for each STUN server */ + for(j=0;j<ctx->stun_server_ct;j++){ + if (ctx->stun_servers[j].transport!=IPPROTO_TCP) + continue; + + if (isock_psv) { + if(r=nr_ice_candidate_create(ctx,component, + isock_psv,isock_psv->sock,SERVER_REFLEXIVE,TCP_TYPE_PASSIVE, + &ctx->stun_servers[j],component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + + if (isock_so) { + if(r=nr_ice_candidate_create(ctx,component, + isock_so,isock_so->sock,SERVER_REFLEXIVE,TCP_TYPE_SO, + &ctx->stun_servers[j],component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + } + } + +#ifdef USE_TURN + /* Create a new relayed candidate for each addr/TURN server pair */ + for(j=0;j<ctx->turn_server_ct;j++){ + nr_transport_addr addr; + nr_socket *local_sock; + nr_socket *buffered_sock; + nr_socket *turn_sock; + nr_ice_socket *turn_isock; + + /* Skip non-TCP */ + if (ctx->turn_servers[j].turn_server.transport != IPPROTO_TCP) + continue; + + if (ctx->turn_servers[j].turn_server.type == NR_ICE_STUN_SERVER_TYPE_ADDR && + nr_transport_addr_cmp(&ctx->turn_servers[j].turn_server.u.addr, + &addrs[i].addr, + NR_TRANSPORT_ADDR_CMP_MODE_VERSION)) { + r_log(LOG_ICE,LOG_INFO,"ICE(%s): Skipping TURN server because of IP version mis-match (%u - %u)",ctx->label,addrs[i].addr.ip_version,ctx->turn_servers[j].turn_server.u.addr.ip_version); + continue; + } + + if (!ice_tcp_disabled) { + /* Use TURN server to get srflx candidates */ + if (isock_psv) { + if(r=nr_ice_candidate_create(ctx,component, + isock_psv,isock_psv->sock,SERVER_REFLEXIVE,TCP_TYPE_PASSIVE, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + + if (isock_so) { + if(r=nr_ice_candidate_create(ctx,component, + isock_so,isock_so->sock,SERVER_REFLEXIVE,TCP_TYPE_SO, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + } + } + + /* Create relay candidate */ + if ((r=nr_transport_addr_copy(&addr, &addrs[i].addr))) + ABORT(r); + addr.protocol = IPPROTO_TCP; + if ((r=nr_transport_addr_fmt_addr_string(&addr))) + ABORT(r); + /* Create a local socket */ + if((r=nr_socket_factory_create_socket(ctx->socket_factory,&addr,&local_sock))){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): couldn't create socket for address %s",ctx->label,addr.as_string); + continue; + } + + r_log(LOG_ICE,LOG_DEBUG,"nr_ice_component_initialize_tcp creating TURN TCP wrappers"); + + if (ctx->turn_tcp_socket_wrapper) { + /* The HTTP proxy socket */ + if((r=nr_socket_wrapper_factory_wrap(ctx->turn_tcp_socket_wrapper, local_sock, &local_sock))) + ABORT(r); + } + + /* The TCP buffered socket */ + if((r=nr_socket_buffered_stun_create(local_sock, NR_STUN_MAX_MESSAGE_SIZE, TURN_TCP_FRAMING, &buffered_sock))) + ABORT(r); + + /* The TURN socket */ + if(r=nr_socket_turn_create(buffered_sock, &turn_sock)) + ABORT(r); + + /* Create an ICE socket */ + if((r=nr_ice_socket_create(ctx, component, buffered_sock, NR_ICE_SOCKET_TYPE_STREAM_TURN, &turn_isock))) + ABORT(r); + + /* Attach ourselves to it */ + if(r=nr_ice_candidate_create(ctx,component, + turn_isock,turn_sock,RELAYED,TCP_TYPE_NONE, + &ctx->turn_servers[j].turn_server,component->component_id,&cand)) + ABORT(r); + cand->u.relayed.srvflx_candidate=NULL; + cand->u.relayed.server=&ctx->turn_servers[j]; + TAILQ_INSERT_TAIL(&component->candidates,cand,entry_comp); + component->candidate_ct++; + cand=0; + + /* Create a STUN server context for this socket */ + if ((r=nr_ice_component_create_stun_server_ctx(component,turn_isock,local_sock,&addr,lufrag,pwd))) + ABORT(r); + + STAILQ_INSERT_TAIL(&component->sockets,turn_isock,entry); + } +#endif /* USE_TURN */ + } + + _status = 0; + abort: + return(_status); + } + + +/* Make all the candidates we can make at the beginning */ +int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component) + { + int r,_status; + nr_local_addr *addrs=ctx->local_addrs; + int addr_ct=ctx->local_addr_ct; + char *lufrag; + char *lpwd; + Data pwd; + nr_ice_candidate *cand; + + if (component->candidate_ct) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): component with id %d already has candidates, probably restarting gathering because of a new stream",ctx->label,component->component_id); + return(0); + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): initializing component with id %d",ctx->label,component->component_id); + + if(addr_ct==0){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): no local addresses available",ctx->label); + ABORT(R_NOT_FOUND); + } + + /* Note: we need to recompute these because + we have not yet computed the values in the peer media stream.*/ + lufrag=component->stream->ufrag ? component->stream->ufrag : ctx->ufrag; + assert(lufrag); + if (!lufrag) + ABORT(R_INTERNAL); + lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd; + assert(lpwd); + if (!lpwd) + ABORT(R_INTERNAL); + INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd)); + + /* Initialize the UDP candidates */ + if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd)) + r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create UDP candidates with error %d",ctx->label,r); + /* And the TCP candidates */ + if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd)) + r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create TCP candidates with error %d",ctx->label,r); + + /* count the candidates that will be initialized */ + cand=TAILQ_FIRST(&component->candidates); + if(!cand){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): couldn't create any valid candidates",ctx->label); + ABORT(R_NOT_FOUND); + } + + while(cand){ + ctx->uninitialized_candidates++; + cand=TAILQ_NEXT(cand,entry_comp); + } + + /* Now initialize all the candidates */ + cand=TAILQ_FIRST(&component->candidates); + while(cand){ + if(cand->state!=NR_ICE_CAND_STATE_INITIALIZING){ + nr_ice_candidate_initialize(cand,nr_ice_gather_finished_cb,cand); + } + cand=TAILQ_NEXT(cand,entry_comp); + } + _status=0; + abort: + return(_status); + } + +static int nr_ice_any_peer_paired(nr_ice_candidate* cand) { + nr_ice_peer_ctx* pctx=STAILQ_FIRST(&cand->ctx->peers); + while(pctx && pctx->state == NR_ICE_PEER_STATE_UNPAIRED){ + /* Is it worth actually looking through the check lists? Probably not. */ + pctx=STAILQ_NEXT(pctx,entry); + } + return pctx != NULL; +} + +/* + Compare this newly initialized candidate against the other initialized + candidates and discard the lower-priority one if they are redundant. + + This algorithm combined with the other algorithms, favors + host > srflx > relay + */ +int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned) + { + nr_ice_candidate *c2, *tmp = NULL; + + *was_pruned = 0; + c2 = TAILQ_FIRST(&comp->candidates); + while(c2){ + if((c1 != c2) && + (c2->state == NR_ICE_CAND_STATE_INITIALIZED) && + !nr_transport_addr_cmp(&c1->base,&c2->base,NR_TRANSPORT_ADDR_CMP_MODE_ALL) && + !nr_transport_addr_cmp(&c1->addr,&c2->addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)){ + + if((c1->type == c2->type) || + (!(ctx->flags & NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES) && + ((c1->type==HOST && c2->type == SERVER_REFLEXIVE) || + (c2->type==HOST && c1->type == SERVER_REFLEXIVE)))){ + + /* + These are redundant. Remove the lower pri one, or if pairing has + already occurred, remove the newest one. + + Since this algorithmis run whenever a new candidate + is initialized, there should at most one duplicate. + */ + if ((c1->priority <= c2->priority) || nr_ice_any_peer_paired(c2)) { + tmp = c1; + *was_pruned = 1; + } + else { + tmp = c2; + } + break; + } + } + + c2=TAILQ_NEXT(c2,entry_comp); + } + + if (tmp) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): Removing redundant candidate", + ctx->label,tmp->label); + + TAILQ_REMOVE(&comp->candidates,tmp,entry_comp); + comp->candidate_ct--; + TAILQ_REMOVE(&tmp->isock->candidates,tmp,entry_sock); + + nr_ice_candidate_destroy(&tmp); + } + + return 0; + } + +static int nr_ice_component_pair_matches_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_transport_addr *local_addr, nr_stun_server_request *req) + { + if(pair->remote->component->component_id!=comp->component_id) + return(0); + + if(nr_transport_addr_cmp(&pair->local->base,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + return(0); + + if(nr_transport_addr_cmp(&pair->remote->addr,&req->src_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + return(0); + + return(1); + } + +static int nr_ice_component_handle_triggered_check(nr_ice_component *comp, nr_ice_cand_pair *pair, nr_stun_server_request *req, int *error) + { + nr_stun_message *sreq=req->request; + int r=0,_status; + + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_USE_CANDIDATE,0)){ + if(comp->stream->pctx->controlling){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND_PAIR(%s): Peer sent USE-CANDIDATE but is controlled",comp->stream->pctx->label, pair->codeword); + } + else{ + /* If this is the first time we've noticed this is nominated...*/ + pair->peer_nominated=1; + + if(pair->state==NR_ICE_PAIR_STATE_SUCCEEDED && !pair->nominated){ + pair->nominated=1; + + if(r=nr_ice_component_nominated_pair(pair->remote->component, pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + } + } + } + + /* Note: the RFC says to trigger first and then nominate. But in that case + * the canceled trigger pair would get nominated and the cloned trigger pair + * would not get the nomination status cloned with it.*/ + if(r=nr_ice_candidate_pair_do_triggered_check(comp->stream->pctx,pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + + _status=0; + abort: + return(r); + } + +/* Section 7.2.1 */ +static int nr_ice_component_process_incoming_check(nr_ice_component *comp, nr_transport_addr *local_addr, nr_stun_server_request *req, int *error) + { + nr_ice_cand_pair *pair; + nr_ice_candidate *pcand=0; + nr_stun_message *sreq=req->request; + nr_stun_message_attribute *attr; + int r=0,_status; + int found_valid=0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): received request from %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,req->src_addr.as_string); + + if (comp->state == NR_ICE_COMPONENT_DISABLED) + ABORT(R_REJECTED); + + /* Check for role conficts (7.2.1.1) */ + if(comp->stream->pctx->controlling){ + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLING,&attr)){ + /* OK, there is a conflict. Who's right? */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlling",comp->stream->pctx->label); + + if(attr->u.ice_controlling > comp->stream->pctx->tiebreaker){ + /* Update the peer ctx. This will propagate to all candidate pairs + in the context. */ + nr_ice_peer_ctx_switch_controlling_role(comp->stream->pctx); + } + else { + /* We are: throw an error */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); + + *error=487; + ABORT(R_REJECTED); + } + } + } + else{ + if(nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_ICE_CONTROLLED,&attr)){ + /* OK, there is a conflict. Who's right? */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): role conflict, both controlled",comp->stream->pctx->label); + + if(attr->u.ice_controlled < comp->stream->pctx->tiebreaker){ + /* Update the peer ctx. This will propagate to all candidate pairs + in the context. */ + nr_ice_peer_ctx_switch_controlling_role(comp->stream->pctx); + } + else { + /* We are: throw an error */ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): returning 487 role conflict",comp->stream->pctx->label); + + *error=487; + ABORT(R_REJECTED); + } + } + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): This STUN request appears to map to local addr %s",comp->stream->pctx->label,local_addr->as_string); + + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + /* Since triggered checks create duplicate pairs (in this implementation) + * we are willing to handle multiple matches here. */ + if(nr_ice_component_pair_matches_check(comp, pair, local_addr, req)){ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND_PAIR(%s): Found a matching pair for received check: %s",comp->stream->pctx->label,pair->codeword,pair->as_string); + if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error)) + ABORT(r); + ++found_valid; + } + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + if(!found_valid){ + /* There were no matching pairs, so we need to create a new peer + * reflexive candidate pair. */ + + if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label); + *error=400; + ABORT(R_BAD_DATA); + } + + /* Find our local component candidate */ + nr_ice_candidate *cand; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): no matching pair",comp->stream->pctx->label); + cand=TAILQ_FIRST(&comp->local_component->candidates); + while(cand){ + if(!nr_transport_addr_cmp(&cand->addr,local_addr,NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + cand=TAILQ_NEXT(cand,entry_comp); + } + + /* Well, this really shouldn't happen, but it's an error from the + other side, so we just throw an error and keep going */ + if(!cand){ + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): stun request to unknown local address %s, discarding",comp->stream->pctx->label,local_addr->as_string); + + *error=400; + ABORT(R_NOT_FOUND); + } + + /* Now make a peer reflexive (remote) candidate */ + if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + pcand->priority=attr->u.priority; + pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + + /* Finally, create the candidate pair, insert into the check list, and + * apply the incoming check to it. */ + if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand, + &pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + if(r=nr_ice_component_insert_pair(comp,pair)) { + *error=(r==R_NO_MEMORY)?500:400; + nr_ice_candidate_pair_destroy(&pair); + ABORT(r); + } + + /* Do this last, since any call to ABORT will destroy pcand */ + TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp); + pcand=0; + + /* Finally start the trigger check if needed */ + if(r=nr_ice_component_handle_triggered_check(comp, pair, req, error)) + ABORT(r); + } + + _status=0; + abort: + if(_status){ + nr_ice_candidate_destroy(&pcand); + assert(*error != 0); + if(r!=R_NO_MEMORY) assert(*error != 500); + } + return(_status); + } + +static int nr_ice_component_stun_server_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error) + { + nr_ice_component *comp=cb_arg; + nr_transport_addr local_addr; + int r,_status; + + if(comp->state==NR_ICE_COMPONENT_FAILED) { + *error=400; + ABORT(R_REJECTED); + } + + /* Find the candidate pair that this maps to */ + if(r=nr_socket_getaddr(sock,&local_addr)) { + *error=500; + ABORT(r); + } + + if (r=nr_ice_component_process_incoming_check(comp, &local_addr, req, error)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced) + { + nr_ice_pre_answer_request *r1,*r2; + nr_ice_component *comp = pcomp->local_component; + int r,_status; + + if (serviced) + *serviced = 0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): looking for pre-answer requests",pctx->label,comp->stream->label,comp->component_id); + + STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) { + if (!strcmp(r1->username, username)) { + int error = 0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): found pre-answer request",pctx->label,comp->stream->label,comp->component_id); + r = nr_ice_component_process_incoming_check(pcomp, &r1->local_addr, &r1->req, &error); + if (r) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): error processing pre-answer request. Would have returned %d",pctx->label,comp->stream->label,comp->component_id, error); + } + (*serviced)++; + STAILQ_REMOVE(&comp->pre_answer_reqs,r1,nr_ice_pre_answer_request_, entry); + nr_ice_pre_answer_request_destroy(&r1); + } + } + + _status=0; + return(_status); + } + +int nr_ice_component_can_candidate_tcptype_pair(nr_socket_tcp_type left, nr_socket_tcp_type right) + { + if (left && !right) + return(0); + if (!left && right) + return(0); + if (left == TCP_TYPE_ACTIVE && right != TCP_TYPE_PASSIVE) + return(0); + if (left == TCP_TYPE_SO && right != TCP_TYPE_SO) + return(0); + if (left == TCP_TYPE_PASSIVE) + return(0); + + return(1); + } + +/* filter out pairings which won't work. */ +int nr_ice_component_can_candidate_addr_pair(nr_transport_addr *local, nr_transport_addr *remote) + { + if(local->ip_version != remote->ip_version) + return(0); + if(nr_transport_addr_is_link_local(local) != + nr_transport_addr_is_link_local(remote)) + return(0); + /* This prevents our ice_unittest (or broken clients) from pairing a + * loopback with a host candidate. */ + if(nr_transport_addr_is_loopback(local) != + nr_transport_addr_is_loopback(remote)) + return(0); + + return(1); + } + +int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote) + { + int r, _status; + nr_ice_candidate *pcand; + nr_ice_cand_pair *pair=0; + char codeword[5]; + + nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing local candidate %s",pctx->label,codeword,lcand->label); + + switch(lcand->type){ + case HOST: + break; + case SERVER_REFLEXIVE: + case PEER_REFLEXIVE: + /* Don't actually pair these candidates */ + goto done; + break; + case RELAYED: + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + TAILQ_FOREACH(pcand, &pcomp->candidates, entry_comp){ + if(!nr_ice_component_can_candidate_addr_pair(&lcand->addr, &pcand->addr)) + continue; + if(!nr_ice_component_can_candidate_tcptype_pair(lcand->tcp_type, pcand->tcp_type)) + continue; + + /* + Two modes, depending on |pair_all_remote| + + 1. Pair remote candidates which have not been paired + (used in initial pairing or in processing the other side's + trickle candidates). + 2. Pair any remote candidate (used when processing our own + trickle candidates). + */ + if (pair_all_remote || (pcand->state == NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED)) { + if (pair_all_remote) { + /* When a remote candidate arrives after the start of checking, but + * before the gathering of local candidates, it can be in UNPAIRED */ + pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + } + + nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): Pairing with peer candidate %s", pctx->label, codeword, pcand->label); + + if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) + ABORT(r); + + if(r=nr_ice_component_insert_pair(pcomp, pair)) + ABORT(r); + } + } + + done: + _status = 0; + abort: + return(_status); + } + +int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp,nr_ice_component *pcomp) + { + nr_ice_candidate *lcand, *pcand; + nr_ice_socket *isock; + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"Pairing candidates======"); + + /* Create the candidate pairs */ + lcand=TAILQ_FIRST(&lcomp->candidates); + + if (!lcand) { + /* No local candidates, initialized or not! */ + ABORT(R_FAILED); + } + + while(lcand){ + if (lcand->state == NR_ICE_CAND_STATE_INITIALIZED) { + if ((r = nr_ice_component_pair_candidate(pctx, pcomp, lcand, 0))) + ABORT(r); + } + + lcand=TAILQ_NEXT(lcand,entry_comp); + } + + /* Mark all peer candidates as paired */ + pcand=TAILQ_FIRST(&pcomp->candidates); + while(pcand){ + pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; + + pcand=TAILQ_NEXT(pcand,entry_comp); + + } + + /* Now register the STUN server callback for this component. + Note that this is a per-component CB so we only need to + do this once. + */ + if (pcomp->state != NR_ICE_COMPONENT_RUNNING) { + isock=STAILQ_FIRST(&lcomp->sockets); + while(isock){ + if(r=nr_stun_server_add_client(isock->stun_server,pctx->label, + pcomp->stream->r2l_user,&pcomp->stream->r2l_pass,nr_ice_component_stun_server_cb,pcomp)) { + ABORT(r); + } + isock=STAILQ_NEXT(isock,entry); + } + } + + pcomp->state = NR_ICE_COMPONENT_RUNNING; + + _status=0; + abort: + return(_status); + } + +int nr_ice_pre_answer_enqueue(nr_ice_component *comp, nr_socket *sock, nr_stun_server_request *req, int *dont_free) + { + int r = 0; + int _status; + nr_ice_pre_answer_request *r1, *r2; + nr_transport_addr dst_addr; + nr_ice_pre_answer_request *par = 0; + + if (r=nr_socket_getaddr(sock, &dst_addr)) + ABORT(r); + + STAILQ_FOREACH_SAFE(r1, &comp->pre_answer_reqs, entry, r2) { + if (!nr_transport_addr_cmp(&r1->local_addr, &dst_addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL) && + !nr_transport_addr_cmp(&r1->req.src_addr, &req->src_addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + return(0); + } + } + + if (r=nr_ice_pre_answer_request_create(&dst_addr, req, &par)) + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG, "ICE(%s)/STREAM(%s)/COMP(%d): Enqueuing STUN request pre-answer from %s", + comp->ctx->label, comp->stream->label, comp->component_id, + req->src_addr.as_string); + + *dont_free = 1; + STAILQ_INSERT_TAIL(&comp->pre_answer_reqs, par, entry); + + _status=0; +abort: + return(_status); + } + +/* Fires when we have an incoming candidate that doesn't correspond to an existing + remote peer. This is either pre-answer or just spurious. Store it in the + component for use when we see the actual answer, at which point we need + to do the procedures from S 7.2.1 in nr_ice_component_stun_server_cb. + */ +static int nr_ice_component_stun_server_default_cb(void *cb_arg,nr_stun_server_ctx *stun_ctx,nr_socket *sock, nr_stun_server_request *req, int *dont_free, int *error) + { + int r, _status; + nr_ice_component *comp = (nr_ice_component *)cb_arg; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Received STUN request pre-answer from %s", + comp->ctx->label, comp->stream->label, comp->component_id, + req->src_addr.as_string); + + if (r=nr_ice_pre_answer_enqueue(comp, sock, req, dont_free)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Failed (%d) to enque pre-answer request from %s", + comp->ctx->label, comp->stream->label, comp->component_id, r, + req->src_addr.as_string); + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +#define NR_ICE_CONSENT_TIMER_DEFAULT 5000 +#define NR_ICE_CONSENT_TIMEOUT_DEFAULT 30000 + +static void nr_ice_component_consent_failed(nr_ice_component *comp) + { + if (!comp->can_send) { + return; + } + + r_log(LOG_ICE,LOG_INFO,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh failed", + comp->ctx->label, comp->stream->label, comp->component_id); + comp->can_send = 0; + + if (comp->consent_timeout) { + NR_async_timer_cancel(comp->consent_timeout); + comp->consent_timeout = 0; + } + if (comp->consent_timer) { + NR_async_timer_cancel(comp->consent_timer); + comp->consent_timer = 0; + } + /* We are turning the consent failure into a ICE component failure to + * alert the browser via ICE connection state change about this event. */ + if (nr_ice_media_stream_component_failed(comp->stream, comp)) + r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): failed to mark component as failed", + comp->ctx->label, comp->stream->label, comp->component_id); + } + +static void nr_ice_component_consent_timeout_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + + comp->consent_timeout = 0; + + r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh final time out", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_consent_failed(comp); + } + + +static void nr_ice_component_consent_request_timed_out(nr_ice_component *comp) + { + if (!comp->can_send) { + return; + } + + nr_ice_peer_ctx_disconnected(comp->stream->pctx); + } + +static void nr_ice_component_consent_refreshed(nr_ice_component *comp) + { + uint16_t tval; + + if (!comp->can_send) { + return; + } + + gettimeofday(&comp->consent_last_seen, 0); + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): consent_last_seen is now %lu", + comp->ctx->label, comp->stream->label, comp->component_id, + comp->consent_last_seen.tv_sec); + + nr_ice_peer_ctx_connected(comp->stream->pctx); + + if (comp->consent_timeout) + NR_async_timer_cancel(comp->consent_timeout); + + tval = NR_ICE_CONSENT_TIMEOUT_DEFAULT; + if (comp->ctx->test_timer_divider) + tval = tval / comp->ctx->test_timer_divider; + + NR_ASYNC_TIMER_SET(tval, nr_ice_component_consent_timeout_cb, comp, + &comp->consent_timeout); + } + +static void nr_ice_component_refresh_consent_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + + switch (comp->consent_ctx->state) { + case NR_STUN_CLIENT_STATE_FAILED: + if (comp->consent_ctx->error_code == 403) { + r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent revoked by peer", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_consent_failed(comp); + } + break; + case NR_STUN_CLIENT_STATE_DONE: + r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent refreshed", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_consent_refreshed(comp); + break; + case NR_STUN_CLIENT_STATE_TIMED_OUT: + r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): A single consent refresh request timed out", + comp->ctx->label, comp->stream->label, comp->component_id); + nr_ice_component_consent_request_timed_out(comp); + break; + default: + break; + } + } + +int nr_ice_component_refresh_consent(nr_stun_client_ctx *ctx, NR_async_cb finished_cb, void *cb_arg) + { + int r,_status; + + nr_stun_client_reset(ctx); + + if (r=nr_stun_client_start(ctx, NR_ICE_CLIENT_MODE_BINDING_REQUEST, finished_cb, cb_arg)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +void nr_ice_component_consent_calc_consent_timer(nr_ice_component *comp) + { + uint16_t trange, trand, tval; + + trange = NR_ICE_CONSENT_TIMER_DEFAULT * 20 / 100; + tval = NR_ICE_CONSENT_TIMER_DEFAULT - trange; + if (!nr_crypto_random_bytes((UCHAR*)&trand, sizeof(trand))) + tval += (trand % (trange * 2)); + + if (comp->ctx->test_timer_divider) + tval = tval / comp->ctx->test_timer_divider; + + /* The timeout of the transaction is the maximum time until we send the + * next consent request. */ + comp->consent_ctx->maximum_transmits_timeout_ms = tval; + } + +static void nr_ice_component_consent_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + int r; + + comp->consent_timer = 0; + + comp->consent_ctx->params.ice_binding_request.username = + comp->stream->l2r_user; + comp->consent_ctx->params.ice_binding_request.password = + comp->stream->l2r_pass; + comp->consent_ctx->params.ice_binding_request.control = + comp->stream->pctx->controlling? + NR_ICE_CONTROLLING:NR_ICE_CONTROLLED; + comp->consent_ctx->params.ice_binding_request.tiebreaker = + comp->stream->pctx->tiebreaker; + comp->consent_ctx->params.ice_binding_request.priority = + comp->active->local->priority; + + nr_ice_component_consent_calc_consent_timer(comp); + + if (r=nr_ice_component_refresh_consent(comp->consent_ctx, + nr_ice_component_refresh_consent_cb, + comp)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Refresh consent failed with %d", + comp->ctx->label, comp->stream->label, comp->component_id, r); + /* In case our attempt to send the refresh binding request reports an + * error we don't have to wait for timeouts, but declare this connection + * dead right away. */ + if (r != R_WOULDBLOCK) { + nr_ice_component_consent_failed(comp); + } + } + + nr_ice_component_consent_schedule_consent_timer(comp); + + } + +void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp) + { + if (!comp->can_send) { + return; + } + + NR_ASYNC_TIMER_SET(comp->consent_ctx->maximum_transmits_timeout_ms, + nr_ice_component_consent_timer_cb, comp, + &comp->consent_timer); + } + +void nr_ice_component_consent_destroy(nr_ice_component *comp) + { + if (comp->consent_timer) { + NR_async_timer_cancel(comp->consent_timer); + comp->consent_timer = 0; + } + if (comp->consent_timeout) { + NR_async_timer_cancel(comp->consent_timeout); + comp->consent_timeout = 0; + } + if (comp->consent_handle) { + nr_ice_socket_deregister(comp->active->local->isock, + comp->consent_handle); + comp->consent_handle = 0; + } + if (comp->consent_ctx) { + nr_stun_client_ctx_destroy(&comp->consent_ctx); + } + } + +int nr_ice_component_setup_consent(nr_ice_component *comp) + { + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Setting up refresh consent", + comp->ctx->label, comp->stream->label, comp->component_id); + + if (r=nr_stun_client_ctx_create("consent", comp->active->local->osock, + &comp->active->remote->addr, 0, + &comp->consent_ctx)) + ABORT(r); + /* Consent request get send only once. */ + comp->consent_ctx->maximum_transmits = 1; + + if (r=nr_ice_socket_register_stun_client(comp->active->local->isock, + comp->consent_ctx, &comp->consent_handle)) + ABORT(r); + + comp->can_send = 1; + nr_ice_component_consent_refreshed(comp); + + nr_ice_component_consent_calc_consent_timer(comp); + nr_ice_component_consent_schedule_consent_timer(comp); + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + int r,_status; + nr_ice_cand_pair *p2; + + /* Are we changing what the nominated pair is? */ + if(comp->nominated){ + if(comp->nominated->priority >= pair->priority) + return(0); + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): replacing pair %s with CAND-PAIR(%s)",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->codeword,comp->nominated->as_string,pair->codeword); + /* As consent doesn't hold a reference to its isock this needs to happen + * before making the new pair the active one. */ + nr_ice_component_consent_destroy(comp); + } + + /* Set the new nominated pair */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): nominated pair is %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string); + comp->state=NR_ICE_COMPONENT_NOMINATED; + comp->nominated=pair; + comp->active=pair; + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling all pairs but %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->codeword,pair->as_string); + + /* Cancel checks in WAITING and FROZEN per ICE S 8.1.2 */ + p2=TAILQ_FIRST(&comp->stream->trigger_check_queue); + while(p2){ + if((p2 != pair) && + (p2->remote->component->component_id == comp->component_id)) { + assert(p2->state == NR_ICE_PAIR_STATE_WAITING || + p2->state == NR_ICE_PAIR_STATE_CANCELLED); + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s in trigger check queue because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword); + + if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2,0)) + ABORT(r); + } + + p2=TAILQ_NEXT(p2,triggered_check_queue_entry); + } + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if((p2 != pair) && + (p2->remote->component->component_id == comp->component_id) && + ((p2->state == NR_ICE_PAIR_STATE_FROZEN) || + (p2->state == NR_ICE_PAIR_STATE_WAITING))) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d)/CAND-PAIR(%s): cancelling FROZEN/WAITING pair %s because CAND-PAIR(%s) was nominated.",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->codeword,p2->as_string,pair->codeword); + + if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2,0)) + ABORT(r); + } + + p2=TAILQ_NEXT(p2,check_queue_entry); + } + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id); + + if(r=nr_ice_component_setup_consent(comp)) + ABORT(r); + + if(r=nr_ice_media_stream_component_nominated(comp->stream,comp)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +static int nr_ice_component_have_all_pairs_failed(nr_ice_component *comp) + { + nr_ice_cand_pair *p2; + + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if(comp->component_id==p2->local->component_id){ + switch(p2->state){ + case NR_ICE_PAIR_STATE_FROZEN: + case NR_ICE_PAIR_STATE_WAITING: + case NR_ICE_PAIR_STATE_IN_PROGRESS: + case NR_ICE_PAIR_STATE_SUCCEEDED: + return(0); + case NR_ICE_PAIR_STATE_FAILED: + case NR_ICE_PAIR_STATE_CANCELLED: + /* states that will never be recovered from */ + break; + default: + assert(0); + break; + } + } + + p2=TAILQ_NEXT(p2,check_queue_entry); + } + + return(1); + } + +int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + return nr_ice_component_check_if_failed(comp); + } + +int nr_ice_component_check_if_failed(nr_ice_component *comp) + { + if (comp->state == NR_ICE_COMPONENT_RUNNING) { + /* Don't do anything to streams that aren't currently running */ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): Checking whether component needs to be marked failed.",comp->stream->pctx->label,comp->stream->label,comp->component_id); + + if (!comp->stream->pctx->trickle_grace_period_timer && + nr_ice_component_have_all_pairs_failed(comp)) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/STREAM(%s)/COMP(%d): All pairs are failed, and grace period has elapsed. Marking component as failed.",comp->stream->pctx->label,comp->stream->label,comp->component_id); + return nr_ice_media_stream_component_failed(comp->stream,comp); + } + } + + return(0); + } + +int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp) + { + nr_ice_cand_pair **pairs=0; + int ct=0; + nr_ice_cand_pair *pair; + int r,_status; + + /* Size the array */ + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + if (comp->component_id == pair->local->component_id) + ct++; + + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + /* Make and fill the array */ + if(!(pairs=RCALLOC(sizeof(nr_ice_cand_pair *)*ct))) + ABORT(R_NO_MEMORY); + + ct=0; + pair=TAILQ_FIRST(&comp->stream->check_list); + while(pair){ + if (comp->component_id == pair->local->component_id) + pairs[ct++]=pair; + + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + if (pctx->handler) { + if(r=pctx->handler->vtbl->select_pair(pctx->handler->obj, + comp->stream,comp->component_id,pairs,ct)) + ABORT(r); + } + + _status=0; + abort: + RFREE(pairs); + return(_status); + } + + +/* Close the underlying sockets for everything but the nominated candidate */ +int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp) + { + nr_ice_socket *isock=0; + nr_ice_socket *s1,*s2; + + if(rcomp->state==NR_ICE_COMPONENT_NOMINATED){ + assert(rcomp->active == rcomp->nominated); + isock=rcomp->nominated->local->isock; + } + + STAILQ_FOREACH_SAFE(s1, &lcomp->sockets, entry, s2){ + if(s1!=isock){ + STAILQ_REMOVE(&lcomp->sockets,s1,nr_ice_socket_,entry); + nr_ice_socket_destroy(&s1); + } + } + + return(0); + } + + +int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair) + { + int r,_status; + + /* Pairs for peer reflexive are marked SUCCEEDED immediately */ + if (pair->state != NR_ICE_PAIR_STATE_FROZEN && + pair->state != NR_ICE_PAIR_STATE_SUCCEEDED){ + assert(0); + ABORT(R_BAD_ARGS); + } + + if(r=nr_ice_candidate_pair_insert(&pair->remote->stream->check_list,pair)) + ABORT(r); + + /* Make sure the check timer is running, if the stream was previously + * started. We will not start streams just because a pair was created, + * unless it is the first pair to be created across all streams. */ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND-PAIR(%s): Ensure that check timer is running for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string); + + if(pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE || + (pair->remote->stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN && + !pair->remote->stream->pctx->checks_started)){ + if(nr_ice_media_stream_start_checks(pair->remote->stream->pctx, pair->remote->stream)) { + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s)/CAND-PAIR(%s): Could not restart checks for new pair %s.",pair->remote->stream->pctx->label, pair->codeword, pair->as_string); + ABORT(R_INTERNAL); + } + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version) + { + int _status; + nr_ice_candidate *cand; + nr_ice_candidate *best_cand = NULL; + + /* We have the component. Now find the "best" candidate, making + use of the fact that more "reliable" candidate types have + higher numbers. So, we sort by type and then priority within + type + */ + cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + if (!nr_ice_ctx_hide_candidate(comp->ctx, cand) && + cand->addr.ip_version == ip_version) { + if (!best_cand) { + best_cand = cand; + } + else if (best_cand->type < cand->type) { + best_cand = cand; + } else if (best_cand->type == cand->type && + best_cand->priority < cand->priority) { + best_cand = cand; + } + } + + cand=TAILQ_NEXT(cand,entry_comp); + } + + /* No candidates */ + if (!best_cand) + ABORT(R_NOT_FOUND); + + *candp = best_cand; + + _status=0; + abort: + return(_status); + + } + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_component.h b/media/mtransport/third_party/nICEr/src/ice/ice_component.h new file mode 100644 index 000000000..5bb0ea1e7 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.h @@ -0,0 +1,104 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_component_h +#define _ice_component_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef struct nr_ice_pre_answer_request_ { + nr_stun_server_request req; + char *username; + nr_transport_addr local_addr; + + STAILQ_ENTRY(nr_ice_pre_answer_request_) entry; +} nr_ice_pre_answer_request; + +typedef STAILQ_HEAD(nr_ice_pre_answer_request_head_, nr_ice_pre_answer_request_) nr_ice_pre_answer_request_head; + +struct nr_ice_component_ { + int state; +#define NR_ICE_COMPONENT_UNPAIRED 0 +#define NR_ICE_COMPONENT_RUNNING 1 +#define NR_ICE_COMPONENT_NOMINATED 2 +#define NR_ICE_COMPONENT_FAILED 3 +#define NR_ICE_COMPONENT_DISABLED 4 + struct nr_ice_ctx_ *ctx; + struct nr_ice_media_stream_ *stream; + nr_ice_component *local_component; + + int component_id; + nr_ice_socket_head sockets; + nr_ice_candidate_head candidates; + int candidate_ct; + nr_ice_pre_answer_request_head pre_answer_reqs; + + int valid_pairs; + struct nr_ice_cand_pair_ *nominated; /* Highest priority nomninated pair */ + struct nr_ice_cand_pair_ *active; + + nr_stun_client_ctx *consent_ctx; + void *consent_timer; + void *consent_timeout; + void *consent_handle; + int can_send; + struct timeval consent_last_seen; + + STAILQ_ENTRY(nr_ice_component_)entry; +}; + +typedef STAILQ_HEAD(nr_ice_component_head_,nr_ice_component_) nr_ice_component_head; + +int nr_ice_component_create(struct nr_ice_media_stream_ *stream, int component_id, nr_ice_component **componentp); +int nr_ice_component_destroy(nr_ice_component **componentp); +int nr_ice_component_initialize(struct nr_ice_ctx_ *ctx,nr_ice_component *component); +int nr_ice_component_maybe_prune_candidate(nr_ice_ctx *ctx, nr_ice_component *comp, nr_ice_candidate *c1, int *was_pruned); +int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote); +int nr_ice_component_pair_candidates(nr_ice_peer_ctx *pctx, nr_ice_component *lcomp, nr_ice_component *pcomp); +int nr_ice_component_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, char *username, int *serviced); +int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair); +int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair); +int nr_ice_component_check_if_failed(nr_ice_component *comp); +int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp); +int nr_ice_component_set_failed(nr_ice_component *comp); +int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp); +int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair); +int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c new file mode 100644 index 000000000..08810a92d --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c @@ -0,0 +1,1057 @@ + +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <csi_platform.h> +#include <assert.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <sys/queue.h> +#include <string.h> +#include <nr_api.h> +#include <registry.h> +#include "stun.h" +#include "ice_ctx.h" +#include "ice_reg.h" +#include "nr_crypto.h" +#include "async_timer.h" +#include "util.h" +#include "nr_socket_local.h" + +#define ICE_UFRAG_LEN 8 +#define ICE_PWD_LEN 32 + +int LOG_ICE = 0; + +static int nr_ice_random_string(char *str, int len); +static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out); +#ifdef USE_TURN +static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out); +#endif /* USE_TURN */ +static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg); +static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand); +static int no_op(void **obj) { + return 0; +} + +static nr_socket_factory_vtbl default_socket_factory_vtbl = { + nr_socket_local_create, + no_op +}; + +int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out) + { + int r,_status; + nr_ice_stun_server *servers = 0; + int i; + NR_registry child; + char *addr=0; + UINT2 port; + in_addr_t addr_int; + + if(!(servers=RCALLOC(sizeof(nr_ice_stun_server)*ct))) + ABORT(R_NO_MEMORY); + + for(i=0;i<ct;i++){ + if(r=NR_reg_get_child_registry(NR_ICE_REG_STUN_SRV_PRFX,i,child)) + ABORT(r); + /* Assume we have a v4 addr for now */ + if(r=NR_reg_alloc2_string(child,"addr",&addr)) + ABORT(r); + addr_int=inet_addr(addr); + if(addr_int==INADDR_NONE){ + r_log(LOG_ICE,LOG_ERR,"Invalid address %s;",addr); + ABORT(R_BAD_ARGS); + } + if(r=NR_reg_get2_uint2(child,"port",&port)) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 3478; + } + if(r=nr_ip4_port_to_transport_addr(ntohl(addr_int), port, IPPROTO_UDP, + &servers[i].u.addr)) + ABORT(r); + servers[i].type = NR_ICE_STUN_SERVER_TYPE_ADDR; + RFREE(addr); + addr=0; + } + + *out = servers; + + _status=0; + abort: + RFREE(addr); + if (_status) RFREE(servers); + return(_status); + } + +int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers,int ct) + { + int _status; + + if(ctx->stun_servers){ + RFREE(ctx->stun_servers); + ctx->stun_server_ct=0; + } + + if (ct) { + if(!(ctx->stun_servers=RCALLOC(sizeof(nr_ice_stun_server)*ct))) + ABORT(R_NO_MEMORY); + + memcpy(ctx->stun_servers,servers,sizeof(nr_ice_stun_server)*ct); + ctx->stun_server_ct = ct; + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers,int ct) + { + int _status; + + if(ctx->turn_servers){ + RFREE(ctx->turn_servers); + ctx->turn_server_ct=0; + } + + if(ct) { + if(!(ctx->turn_servers=RCALLOC(sizeof(nr_ice_turn_server)*ct))) + ABORT(R_NO_MEMORY); + + memcpy(ctx->turn_servers,servers,sizeof(nr_ice_turn_server)*ct); + ctx->turn_server_ct = ct; + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_copy_turn_servers(nr_ice_ctx *ctx, nr_ice_turn_server *servers, int ct) + { + int _status, i, r; + + if (r = nr_ice_ctx_set_turn_servers(ctx, servers, ct)) { + ABORT(r); + } + + // make copies of the username and password so they aren't freed twice + for (i = 0; i < ct; ++i) { + if (!(ctx->turn_servers[i].username = r_strdup(servers[i].username))) { + ABORT(R_NO_MEMORY); + } + if (r = r_data_create(&ctx->turn_servers[i].password, + servers[i].password->data, + servers[i].password->len)) { + ABORT(r); + } + } + + _status=0; + abort: + return(_status); + } + +static int nr_ice_ctx_set_local_addrs(nr_ice_ctx *ctx,nr_local_addr *addrs,int ct) + { + int _status,i,r; + + if(ctx->local_addrs) { + RFREE(ctx->local_addrs); + ctx->local_addr_ct=0; + ctx->local_addrs=0; + } + + if (ct) { + if(!(ctx->local_addrs=RCALLOC(sizeof(nr_local_addr)*ct))) + ABORT(R_NO_MEMORY); + + for (i=0;i<ct;++i) { + if (r=nr_local_addr_copy(ctx->local_addrs+i,addrs+i)) { + ABORT(r); + } + } + ctx->local_addr_ct = ct; + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver) + { + int _status; + + if (ctx->resolver) { + ABORT(R_ALREADY); + } + + ctx->resolver = resolver; + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *ip) + { + int _status; + + if (ctx->interface_prioritizer) { + ABORT(R_ALREADY); + } + + ctx->interface_prioritizer = ip; + + _status=0; + abort: + return(_status); + } + +int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper) + { + int _status; + + if (ctx->turn_tcp_socket_wrapper) { + ABORT(R_ALREADY); + } + + ctx->turn_tcp_socket_wrapper = wrapper; + + _status=0; + abort: + return(_status); + } + +void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory) + { + nr_socket_factory_destroy(&ctx->socket_factory); + ctx->socket_factory = factory; + } + +#ifdef USE_TURN +int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out) + { + int r,_status; + nr_ice_turn_server *servers = 0; + int i; + NR_registry child; + char *addr=0; + UINT2 port; + in_addr_t addr_int; + Data data={0}; + + if(!(servers=RCALLOC(sizeof(nr_ice_turn_server)*ct))) + ABORT(R_NO_MEMORY); + + for(i=0;i<ct;i++){ + if(r=NR_reg_get_child_registry(NR_ICE_REG_TURN_SRV_PRFX,i,child)) + ABORT(r); + /* Assume we have a v4 addr for now */ + if(r=NR_reg_alloc2_string(child,"addr",&addr)) + ABORT(r); + addr_int=inet_addr(addr); + if(addr_int==INADDR_NONE){ + r_log(LOG_ICE,LOG_ERR,"Invalid address %s",addr); + ABORT(R_BAD_ARGS); + } + if(r=NR_reg_get2_uint2(child,"port",&port)) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 3478; + } + if(r=nr_ip4_port_to_transport_addr(ntohl(addr_int), port, IPPROTO_UDP, + &servers[i].turn_server.u.addr)) + ABORT(r); + + + if(r=NR_reg_alloc2_string(child,NR_ICE_REG_TURN_SRV_USERNAME,&servers[i].username)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + if(r=NR_reg_alloc2_data(child,NR_ICE_REG_TURN_SRV_PASSWORD,&data)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + else { + servers[i].password=RCALLOC(sizeof(*servers[i].password)); + if(!servers[i].password) + ABORT(R_NO_MEMORY); + servers[i].password->data = data.data; + servers[i].password->len = data.len; + data.data=0; + } + + RFREE(addr); + addr=0; + } + + *out = servers; + + _status=0; + abort: + RFREE(data.data); + RFREE(addr); + if (_status) RFREE(servers); + return(_status); + } +#endif /* USE_TURN */ + +#define MAXADDRS 100 /* Ridiculously high */ +int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp) + { + int r,_status; + char *ufrag = 0; + char *pwd = 0; + + if (r=nr_ice_get_new_ice_ufrag(&ufrag)) + ABORT(r); + if (r=nr_ice_get_new_ice_pwd(&pwd)) + ABORT(r); + + if (r=nr_ice_ctx_create_with_credentials(label, flags, ufrag, pwd, ctxp)) + ABORT(r); + + _status=0; + abort: + RFREE(ufrag); + RFREE(pwd); + + return(_status); + } + +int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char *ufrag, char *pwd, nr_ice_ctx **ctxp) + { + nr_ice_ctx *ctx=0; + int r,_status; + + if(r=r_log_register("ice", &LOG_ICE)) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_ice_ctx)))) + ABORT(R_NO_MEMORY); + + ctx->flags=flags; + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + if(!(ctx->ufrag=r_strdup(ufrag))) + ABORT(r); + if(!(ctx->pwd=r_strdup(pwd))) + ABORT(r); + + /* Get the STUN servers */ + if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX, + (unsigned int *)&ctx->stun_server_ct)||ctx->stun_server_ct==0) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No STUN servers specified in nICEr registry", ctx->label); + ctx->stun_server_ct=0; + } + + /* 31 is the max for our priority algorithm */ + if(ctx->stun_server_ct>31){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN servers specified: max=31", ctx->label); + ctx->stun_server_ct=31; + } + + if(ctx->stun_server_ct>0){ + if(r=nr_ice_fetch_stun_servers(ctx->stun_server_ct,&ctx->stun_servers)){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load STUN servers from registry", ctx->label); + ctx->stun_server_ct=0; + ABORT(r); + } + } + +#ifdef USE_TURN + /* Get the TURN servers */ + if(r=NR_reg_get_child_count(NR_ICE_REG_TURN_SRV_PRFX, + (unsigned int *)&ctx->turn_server_ct)||ctx->turn_server_ct==0) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No TURN servers specified in nICEr registry", ctx->label); + ctx->turn_server_ct=0; + } +#else + ctx->turn_server_ct=0; +#endif /* USE_TURN */ + + ctx->local_addrs=0; + ctx->local_addr_ct=0; + + /* 31 is the max for our priority algorithm */ + if((ctx->stun_server_ct+ctx->turn_server_ct)>31){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Too many STUN/TURN servers specified: max=31", ctx->label); + ctx->turn_server_ct=31-ctx->stun_server_ct; + } + +#ifdef USE_TURN + if(ctx->turn_server_ct>0){ + if(r=nr_ice_fetch_turn_servers(ctx->turn_server_ct,&ctx->turn_servers)){ + ctx->turn_server_ct=0; + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Couldn't load TURN servers from registry", ctx->label); + ABORT(r); + } + } +#endif /* USE_TURN */ + + + ctx->Ta = 20; + + ctx->test_timer_divider = 0; + + if (r=nr_socket_factory_create_int(NULL, &default_socket_factory_vtbl, &ctx->socket_factory)) + ABORT(r); + + if ((r=NR_reg_get_string((char *)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME, ctx->force_net_interface, sizeof(ctx->force_net_interface)))) { + if (r == R_NOT_FOUND) { + ctx->force_net_interface[0] = 0; + } else { + ABORT(r); + } + } + + STAILQ_INIT(&ctx->streams); + STAILQ_INIT(&ctx->sockets); + STAILQ_INIT(&ctx->foundations); + STAILQ_INIT(&ctx->peers); + STAILQ_INIT(&ctx->ids); + + *ctxp=ctx; + + _status=0; + abort: + if(_status && ctx) + nr_ice_ctx_destroy_cb(0,0,ctx); + + return(_status); + } + +static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_ctx *ctx=cb_arg; + nr_ice_foundation *f1,*f2; + nr_ice_media_stream *s1,*s2; + int i; + nr_ice_stun_id *id1,*id2; + + STAILQ_FOREACH_SAFE(s1, &ctx->streams, entry, s2){ + STAILQ_REMOVE(&ctx->streams,s1,nr_ice_media_stream_,entry); + nr_ice_media_stream_destroy(&s1); + } + + RFREE(ctx->label); + + RFREE(ctx->stun_servers); + + RFREE(ctx->local_addrs); + + for (i = 0; i < ctx->turn_server_ct; i++) { + RFREE(ctx->turn_servers[i].username); + r_data_destroy(&ctx->turn_servers[i].password); + } + RFREE(ctx->turn_servers); + + f1=STAILQ_FIRST(&ctx->foundations); + while(f1){ + f2=STAILQ_NEXT(f1,entry); + RFREE(f1); + f1=f2; + } + RFREE(ctx->pwd); + RFREE(ctx->ufrag); + + STAILQ_FOREACH_SAFE(id1, &ctx->ids, entry, id2){ + STAILQ_REMOVE(&ctx->ids,id1,nr_ice_stun_id_,entry); + RFREE(id1); + } + + nr_resolver_destroy(&ctx->resolver); + nr_interface_prioritizer_destroy(&ctx->interface_prioritizer); + nr_socket_wrapper_factory_destroy(&ctx->turn_tcp_socket_wrapper); + nr_socket_factory_destroy(&ctx->socket_factory); + + RFREE(ctx); + } + +void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags) + { + ctx->flags |= flags; + } + +void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags) + { + ctx->flags &= ~flags; + } + +int nr_ice_ctx_destroy(nr_ice_ctx **ctxp) + { + if(!ctxp || !*ctxp) + return(0); + + (*ctxp)->done_cb=0; + (*ctxp)->trickle_cb=0; + + NR_ASYNC_SCHEDULE(nr_ice_ctx_destroy_cb,*ctxp); + + *ctxp=0; + + return(0); + } + +void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg) + { + int r; + nr_ice_candidate *cand=cb_arg; + nr_ice_ctx *ctx; + + + assert(cb_arg); + if (!cb_arg) + return; + ctx = cand->ctx; + + ctx->uninitialized_candidates--; + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/CAND(%s): initialized, %d remaining",ctx->label,cand->codeword,ctx->uninitialized_candidates); + + /* Avoid the need for yet another initialization function */ + if (cand->state == NR_ICE_CAND_STATE_INITIALIZING && cand->type == HOST) + cand->state = NR_ICE_CAND_STATE_INITIALIZED; + + if (cand->state == NR_ICE_CAND_STATE_INITIALIZED) { + int was_pruned = 0; + + if (r=nr_ice_component_maybe_prune_candidate(ctx, cand->component, + cand, &was_pruned)) { + r_log(LOG_ICE, LOG_NOTICE, "ICE(%s): Problem pruning candidates",ctx->label); + } + + /* If we are initialized, the candidate wasn't pruned, + and we have a trickle ICE callback fire the callback */ + if (ctx->trickle_cb && !was_pruned && + !nr_ice_ctx_hide_candidate(ctx, cand)) { + ctx->trickle_cb(ctx->trickle_cb_arg, ctx, cand->stream, cand->component_id, cand); + + if (nr_ice_ctx_pair_new_trickle_candidates(ctx, cand)) { + r_log(LOG_ICE,LOG_ERR, "ICE(%s): All could not pair new trickle candidate",ctx->label); + /* But continue */ + } + } + } + + if(ctx->uninitialized_candidates==0){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label); + if (ctx->done_cb) { + ctx->done_cb(0,0,ctx->cb_arg); + } + else { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No done_cb. We were probably destroyed.",ctx->label); + } + } + else { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Waiting for %d candidates to be initialized",ctx->label, ctx->uninitialized_candidates); + } + } + +static int nr_ice_ctx_pair_new_trickle_candidates(nr_ice_ctx *ctx, nr_ice_candidate *cand) + { + int r,_status; + nr_ice_peer_ctx *pctx; + + pctx=STAILQ_FIRST(&ctx->peers); + while(pctx){ + if (pctx->state == NR_ICE_PEER_STATE_PAIRED) { + r = nr_ice_peer_ctx_pair_new_trickle_candidate(ctx, pctx, cand); + if (r) + ABORT(r); + } + + pctx=STAILQ_NEXT(pctx,entry); + } + + _status=0; + abort: + return(_status); + } + +/* Get the default address by doing a connect to a known public IP address, + in this case Google public DNS: + + IPv4: 8.8.8.8 + IPv6: 2001:4860:4860::8888 + + Then we can do getsockname to get the address. No packets get sent + since this is UDP. It's just a way to get the address. +*/ +static int nr_ice_get_default_address(nr_ice_ctx *ctx, int ip_version, nr_transport_addr* addrp) + { + int r,_status; + nr_transport_addr addr; + nr_transport_addr remote_addr; + nr_socket *sock=0; + + switch(ip_version) { + case NR_IPV4: + if ((r=nr_str_port_to_transport_addr("0.0.0.0", 0, IPPROTO_UDP, &addr))) + ABORT(r); + if ((r=nr_str_port_to_transport_addr("8.8.8.8", 53, IPPROTO_UDP, &remote_addr))) + ABORT(r); + break; + case NR_IPV6: + if ((r=nr_str_port_to_transport_addr("::0", 0, IPPROTO_UDP, &addr))) + ABORT(r); + if ((r=nr_str_port_to_transport_addr("2001:4860:4860::8888", 53, IPPROTO_UDP, &remote_addr))) + ABORT(r); + break; + default: + assert(0); + ABORT(R_INTERNAL); + } + + if ((r=nr_socket_factory_create_socket(ctx->socket_factory, &addr, &sock))) + ABORT(r); + if ((r=nr_socket_connect(sock, &remote_addr))) + ABORT(r); + if ((r=nr_socket_getaddr(sock, addrp))) + ABORT(r); + + r_log(LOG_GENERIC, LOG_DEBUG, "Default address: %s", addrp->as_string); + + _status=0; + abort: + nr_socket_destroy(&sock); + return(_status); + } + +static int nr_ice_get_default_local_address(nr_ice_ctx *ctx, int ip_version, nr_local_addr* addrs, int addr_ct, nr_local_addr *addrp) + { + int r,_status; + nr_transport_addr default_addr; + int i; + + if ((r=nr_ice_get_default_address(ctx, ip_version, &default_addr))) + ABORT(r); + + for (i=0; i < addr_ct; ++i) { + if (!nr_transport_addr_cmp(&default_addr, &addrs[i].addr, + NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) { + if ((r=nr_local_addr_copy(addrp, &addrs[i]))) + ABORT(r); + break; + } + } + + if (i == addr_ct) { + if ((r=nr_transport_addr_copy(&addrp->addr, &default_addr))) + ABORT(r); + strlcpy(addrp->addr.ifname, "default route", sizeof(addrp->addr.ifname)); + } + + _status=0; + abort: + return(_status); + } + +static int nr_ice_get_local_addresses(nr_ice_ctx *ctx) + { + int r,_status; + nr_local_addr local_addrs[MAXADDRS]; + nr_local_addr *addrs = 0; + int i,addr_ct; + nr_local_addr default_addrs[2]; + int default_addr_ct = 0; + + if (!ctx->local_addrs) { + /* First, gather all the local addresses we have */ + if((r=nr_stun_find_local_addresses(local_addrs,MAXADDRS,&addr_ct))) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to gather local addresses, trying default route",ctx->label); + } + + if (ctx->force_net_interface[0] && addr_ct) { + /* Limit us to only addresses on a single interface */ + int force_addr_ct = 0; + for(i=0;i<addr_ct;i++){ + if (!strcmp(local_addrs[i].addr.ifname, ctx->force_net_interface)) { + // copy it down in the array, if needed + if (i != force_addr_ct) { + if (r=nr_local_addr_copy(&local_addrs[force_addr_ct], &local_addrs[i])) { + ABORT(r); + } + } + force_addr_ct++; + } + } + addr_ct = force_addr_ct; + } + + if ((!addr_ct) || (ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS)) { + /* Get just the default IPv4 and IPv6 addrs */ + if(!nr_ice_get_default_local_address(ctx, NR_IPV4, local_addrs, addr_ct, + &default_addrs[default_addr_ct])) { + ++default_addr_ct; + } + if(!nr_ice_get_default_local_address(ctx, NR_IPV6, local_addrs, addr_ct, + &default_addrs[default_addr_ct])) { + ++default_addr_ct; + } + if (!default_addr_ct) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): failed to find default addresses",ctx->label); + ABORT(R_FAILED); + } + addrs = default_addrs; + addr_ct = default_addr_ct; + } + else { + addrs = local_addrs; + } + + /* Sort interfaces by preference */ + if(ctx->interface_prioritizer) { + for(i=0;i<addr_ct;i++){ + if(r=nr_interface_prioritizer_add_interface(ctx->interface_prioritizer,addrs+i)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to add interface ",ctx->label); + ABORT(r); + } + } + if(r=nr_interface_prioritizer_sort_preference(ctx->interface_prioritizer)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): unable to sort interface by preference",ctx->label); + ABORT(r); + } + } + + if (r=nr_ice_ctx_set_local_addrs(ctx,addrs,addr_ct)) { + ABORT(r); + } + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg) + { + int r,_status; + nr_ice_media_stream *stream; + + if ((r=nr_ice_get_local_addresses(ctx))) + ABORT(r); + + if(STAILQ_EMPTY(&ctx->streams)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Missing streams to initialize",ctx->label); + ABORT(R_BAD_ARGS); + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Initializing candidates",ctx->label); + ctx->done_cb=done_cb; + ctx->cb_arg=cb_arg; + + /* Initialize all the media stream/component pairs */ + stream=STAILQ_FIRST(&ctx->streams); + while(stream){ + if(r=nr_ice_media_stream_initialize(ctx,stream)) + ABORT(r); + + stream=STAILQ_NEXT(stream,entry); + } + + if(ctx->uninitialized_candidates) + ABORT(R_WOULDBLOCK); + + _status=0; + abort: + return(_status); + } + +int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp) + { + int r,_status; + + if(r=nr_ice_media_stream_create(ctx,label,components,streamp)) + ABORT(r); + + STAILQ_INSERT_TAIL(&ctx->streams,*streamp,entry); + + _status=0; + abort: + return(_status); + } + +int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp) + { + int r,_status; + nr_ice_peer_ctx *pctx; + nr_ice_media_stream *peer_stream; + + pctx=STAILQ_FIRST(&ctx->peers); + while(pctx){ + if(!nr_ice_peer_ctx_find_pstream(pctx, *streamp, &peer_stream)) { + if(r=nr_ice_peer_ctx_remove_pstream(pctx, &peer_stream)) { + ABORT(r); + } + } + + pctx=STAILQ_NEXT(pctx,entry); + } + + STAILQ_REMOVE(&ctx->streams,*streamp,nr_ice_media_stream_,entry); + if(r=nr_ice_media_stream_destroy(streamp)) { + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp) + { + char **attrs=0; + int _status; + char *tmp=0; + + if(!(attrs=RCALLOC(sizeof(char *)*2))) + ABORT(R_NO_MEMORY); + + if(!(tmp=RMALLOC(100))) + ABORT(R_NO_MEMORY); + snprintf(tmp,100,"ice-ufrag:%s",ctx->ufrag); + attrs[0]=tmp; + + if(!(tmp=RMALLOC(100))) + ABORT(R_NO_MEMORY); + snprintf(tmp,100,"ice-pwd:%s",ctx->pwd); + attrs[1]=tmp; + + *attrctp=2; + *attrsp=attrs; + + _status=0; + abort: + if (_status){ + if (attrs){ + RFREE(attrs[0]); + RFREE(attrs[1]); + } + RFREE(attrs); + } + return(_status); + } + +static int nr_ice_random_string(char *str, int len) + { + unsigned char bytes[100]; + int needed; + int r,_status; + + if(len%2) ABORT(R_BAD_ARGS); + needed=len/2; + + if(needed>sizeof(bytes)) ABORT(R_BAD_ARGS); + + if(r=nr_crypto_random_bytes(bytes,needed)) + ABORT(r); + + if(r=nr_bin2hex(bytes,needed,(unsigned char *)str)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +/* This is incredibly annoying: we now have a datagram but we don't + know which peer it's from, and we need to be able to tell the + API user. So, offer it to each peer and if one bites, assume + the others don't want it +*/ +int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len) + { + nr_ice_peer_ctx *pctx; + int r; + + pctx=STAILQ_FIRST(&ctx->peers); + while(pctx){ + r=nr_ice_peer_ctx_deliver_packet_maybe(pctx, comp, source_addr, data, len); + if(!r) + break; + + pctx=STAILQ_NEXT(pctx,entry); + } + + if(!pctx) + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Packet received from %s which doesn't match any known peer",ctx->label,source_addr->as_string); + + return(0); + } + +int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]) + { + nr_ice_stun_id *xid; + + xid=STAILQ_FIRST(&ctx->ids); + while(xid){ + if (!memcmp(xid->id, id, 12)) + return 1; + + xid=STAILQ_NEXT(xid,entry); + } + + return 0; + } + +int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg) +{ + int _status; + nr_ice_stun_id *xid; + + xid = RCALLOC(sizeof(*xid)); + if (!xid) + ABORT(R_NO_MEMORY); + + assert(sizeof(xid->id) == sizeof(msg->header.id)); +#if __STDC_VERSION__ >= 201112L + _Static_assert(sizeof(xid->id) == sizeof(msg->header.id),"Message ID Size Mismatch"); +#endif + memcpy(xid->id, &msg->header.id, sizeof(xid->id)); + + STAILQ_INSERT_TAIL(&ctx->ids,xid,entry); + + _status=0; + abort: + return(_status); +} + + +/* Clean up some of the resources (mostly file descriptors) used + by candidates we didn't choose. Note that this still leaves + a fair amount of non-system stuff floating around. This gets + cleaned up when you destroy the ICE ctx */ +int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *lstr,*rstr; + + r_log(LOG_ICE,LOG_DEBUG,"Finalizing ICE ctx %s, peer=%s",ctx->label,pctx->label); + /* + First find the peer stream, if any + */ + lstr=STAILQ_FIRST(&ctx->streams); + while(lstr){ + rstr=STAILQ_FIRST(&pctx->peer_streams); + + while(rstr){ + if(rstr->local_stream==lstr) + break; + + rstr=STAILQ_NEXT(rstr,entry); + } + + nr_ice_media_stream_finalize(lstr,rstr); + + lstr=STAILQ_NEXT(lstr,entry); + } + + return(0); + } + + +int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg) +{ + ctx->trickle_cb = cb; + ctx->trickle_cb_arg = cb_arg; + + return 0; +} + +int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand) + { + if (cand->state != NR_ICE_CAND_STATE_INITIALIZED) { + return 1; + } + + if (ctx->flags & NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES) { + if (cand->type == HOST) + return 1; + } + + return 0; + } + +int nr_ice_get_new_ice_ufrag(char** ufrag) + { + int r,_status; + char buf[ICE_UFRAG_LEN+1]; + + if(r=nr_ice_random_string(buf,ICE_UFRAG_LEN)) + ABORT(r); + if(!(*ufrag=r_strdup(buf))) + ABORT(r); + + _status=0; + abort: + if(_status) { + RFREE(*ufrag); + *ufrag = 0; + } + return(_status); + } + +int nr_ice_get_new_ice_pwd(char** pwd) + { + int r,_status; + char buf[ICE_PWD_LEN+1]; + + if(r=nr_ice_random_string(buf,ICE_PWD_LEN)) + ABORT(r); + if(!(*pwd=r_strdup(buf))) + ABORT(r); + + _status=0; + abort: + if(_status) { + RFREE(*pwd); + *pwd = 0; + } + return(_status); + } + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h new file mode 100644 index 000000000..cc79304f0 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h @@ -0,0 +1,203 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_ctx_h +#define _ice_ctx_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +/* Not good practice but making includes simpler */ +#include "transport_addr.h" +#include "nr_socket.h" +#include "nr_resolver.h" +#include "nr_interface_prioritizer.h" +#include "nr_socket_wrapper.h" +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "turn_client_ctx.h" + +#define NR_ICE_STUN_SERVER_TYPE_ADDR 1 +#define NR_ICE_STUN_SERVER_TYPE_DNSNAME 2 + +typedef struct nr_ice_stun_server_ { + int type; + union { + nr_transport_addr addr; + struct { + char host[256]; /* Limit from RFC 1034, plus a 0 byte */ + UINT2 port; + } dnsname; + } u; + int id; + int transport; +} nr_ice_stun_server; + +typedef struct nr_ice_turn_server_ { + nr_ice_stun_server turn_server; + char *username; + Data *password; +} nr_ice_turn_server; + +typedef struct nr_ice_foundation_ { + int index; + + nr_transport_addr addr; + int type; + nr_ice_stun_server *stun_server; + + STAILQ_ENTRY(nr_ice_foundation_) entry; +} nr_ice_foundation; + +typedef STAILQ_HEAD(nr_ice_foundation_head_,nr_ice_foundation_) nr_ice_foundation_head; + +typedef TAILQ_HEAD(nr_ice_candidate_head_,nr_ice_candidate_) nr_ice_candidate_head; +typedef TAILQ_HEAD(nr_ice_cand_pair_head_,nr_ice_cand_pair_) nr_ice_cand_pair_head; +typedef struct nr_ice_component_ nr_ice_component; +typedef struct nr_ice_media_stream_ nr_ice_media_stream; +typedef struct nr_ice_ctx_ nr_ice_ctx; +typedef struct nr_ice_peer_ctx_ nr_ice_peer_ctx; +typedef struct nr_ice_candidate_ nr_ice_candidate; +typedef struct nr_ice_cand_pair_ nr_ice_cand_pair; +typedef void (*nr_ice_trickle_candidate_cb) (void *cb_arg, + nr_ice_ctx *ctx, nr_ice_media_stream *stream, int component_id, + nr_ice_candidate *candidate); + +#include "ice_socket.h" +#include "ice_component.h" +#include "ice_media_stream.h" +#include "ice_candidate.h" +#include "ice_candidate_pair.h" +#include "ice_handler.h" +#include "ice_peer_ctx.h" + +typedef struct nr_ice_stun_id_ { + UCHAR id[12]; + + STAILQ_ENTRY(nr_ice_stun_id_) entry; +} nr_ice_stun_id; + +typedef STAILQ_HEAD(nr_ice_stun_id_head_,nr_ice_stun_id_) nr_ice_stun_id_head; + +struct nr_ice_ctx_ { + UINT4 flags; + char *label; + + char *ufrag; + char *pwd; + + UINT4 Ta; + + nr_ice_stun_server *stun_servers; /* The list of stun servers */ + int stun_server_ct; + nr_ice_turn_server *turn_servers; /* The list of turn servers */ + int turn_server_ct; + nr_local_addr *local_addrs; /* The list of available local addresses and corresponding interface information */ + int local_addr_ct; + + nr_resolver *resolver; /* The resolver to use */ + nr_interface_prioritizer *interface_prioritizer; /* Priority decision logic */ + nr_socket_wrapper_factory *turn_tcp_socket_wrapper; /* The TURN TCP socket wrapper to use */ + nr_socket_factory *socket_factory; + + nr_ice_foundation_head foundations; + + nr_ice_media_stream_head streams; /* Media streams */ + int stream_ct; + nr_ice_socket_head sockets; /* The sockets we're using */ + int uninitialized_candidates; + + UINT4 gather_rto; + UINT4 stun_delay; + + UINT4 test_timer_divider; + + nr_ice_peer_ctx_head peers; + nr_ice_stun_id_head ids; + + NR_async_cb done_cb; + void *cb_arg; + + nr_ice_trickle_candidate_cb trickle_cb; + void *trickle_cb_arg; + + char force_net_interface[MAXIFNAME]; +}; + +int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp); +int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char* ufrag, char* pwd, nr_ice_ctx **ctxp); +#define NR_ICE_CTX_FLAGS_OFFERER 1 +#define NR_ICE_CTX_FLAGS_ANSWERER (1<<1) +#define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1<<2) +#define NR_ICE_CTX_FLAGS_LITE (1<<3) +#define NR_ICE_CTX_FLAGS_RELAY_ONLY (1<<4) +#define NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES (1<<5) +#define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS (1<<6) +#define NR_ICE_CTX_FLAGS_ONLY_PROXY (1<<7) + +void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags); +void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags); +int nr_ice_ctx_destroy(nr_ice_ctx **ctxp); +int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg); +int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand); +void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg); +int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp); +int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp); +int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp); +int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); +int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]); +int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg); +int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx); +int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers, int ct); +int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int ct); +int nr_ice_ctx_copy_turn_servers(nr_ice_ctx *ctx, nr_ice_turn_server *servers, int ct); +int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver); +int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *prioritizer); +int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper); +void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory); +int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg); +int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand); +int nr_ice_get_new_ice_ufrag(char** ufrag); +int nr_ice_get_new_ice_pwd(char** pwd); + +#define NR_ICE_MAX_ATTRIBUTE_SIZE 256 + +extern int LOG_ICE; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_handler.h b/media/mtransport/third_party/nICEr/src/ice/ice_handler.h new file mode 100644 index 000000000..5a0690ada --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_handler.h @@ -0,0 +1,84 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_h +#define _ice_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef struct nr_ice_handler_vtbl_ { + /* The checks on this media stream are done. The handler needs to + select a single pair to proceed with (regular nomination). + Once this returns the check starts and the pair can be + written on. Use nr_ice_candidate_pair_select() to perform the + selection. + TODO: !ekr! is this right? + */ + int (*select_pair)(void *obj,nr_ice_media_stream *stream, +int component_id, nr_ice_cand_pair **potentials,int potential_ct); + + /* This media stream is ready to read/write (aggressive nomination). + May be called again if the nominated pair changes due to + ICE instability. TODO: !ekr! think about this + */ + int (*stream_ready)(void *obj, nr_ice_media_stream *stream); + + /* This media stream has failed */ + int (*stream_failed)(void *obj, nr_ice_media_stream *stream); + + /* ICE is connected for this peer ctx */ + int (*ice_connected)(void *obj, nr_ice_peer_ctx *pctx); + + /* A message was delivered to us */ + int (*msg_recvd)(void *obj, nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, int component_id, UCHAR *msg, int len); + + /* ICE has started checking. */ + int (*ice_checking)(void *obj, nr_ice_peer_ctx *pctx); + + /* ICE detected a (temporary?) disconnect. */ + int (*ice_disconnected)(void *obj, nr_ice_peer_ctx *pctx); +} nr_ice_handler_vtbl; + +typedef struct nr_ice_handler_ { + void *obj; + nr_ice_handler_vtbl *vtbl; +} nr_ice_handler; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c new file mode 100644 index 000000000..b93e45aeb --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c @@ -0,0 +1,933 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_media_stream.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <string.h> +#include <assert.h> +#include <nr_api.h> +#include <r_assoc.h> +#include <async_timer.h> +#include "ice_util.h" +#include "ice_ctx.h" + +static char *nr_ice_media_stream_states[]={"INVALID", + "UNPAIRED","FROZEN","ACTIVE","COMPLETED","FAILED" +}; + +int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); + +int nr_ice_media_stream_create(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp) + { + int r,_status; + nr_ice_media_stream *stream=0; + nr_ice_component *comp=0; + int i; + + if(!(stream=RCALLOC(sizeof(nr_ice_media_stream)))) + ABORT(R_NO_MEMORY); + + if(!(stream->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + stream->ctx=ctx; + + STAILQ_INIT(&stream->components); + for(i=0;i<components;i++){ + /* component-id must be > 0, so increment by 1 */ + if(r=nr_ice_component_create(stream, i+1, &comp)) + ABORT(r); + + } + + TAILQ_INIT(&stream->check_list); + TAILQ_INIT(&stream->trigger_check_queue); + + stream->component_ct=components; + stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED; + *streamp=stream; + + _status=0; + abort: + if(_status){ + nr_ice_media_stream_destroy(&stream); + } + return(_status); + } + +int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp) + { + nr_ice_media_stream *stream; + nr_ice_component *c1,*c2; + nr_ice_cand_pair *p1,*p2; + if(!streamp || !*streamp) + return(0); + + stream=*streamp; + *streamp=0; + + STAILQ_FOREACH_SAFE(c1, &stream->components, entry, c2){ + STAILQ_REMOVE(&stream->components,c1,nr_ice_component_,entry); + nr_ice_component_destroy(&c1); + } + + /* Note: all the entries from the trigger check queue are held in here as + * well, so we only clean up the super set. */ + TAILQ_FOREACH_SAFE(p1, &stream->check_list, check_queue_entry, p2){ + TAILQ_REMOVE(&stream->check_list,p1,check_queue_entry); + nr_ice_candidate_pair_destroy(&p1); + } + + RFREE(stream->label); + + RFREE(stream->ufrag); + RFREE(stream->pwd); + RFREE(stream->r2l_user); + RFREE(stream->l2r_user); + r_data_zfree(&stream->r2l_pass); + r_data_zfree(&stream->l2r_pass); + + if(stream->timer) + NR_async_timer_cancel(stream->timer); + + RFREE(stream); + + return(0); + } + +int nr_ice_media_stream_initialize(nr_ice_ctx *ctx, nr_ice_media_stream *stream) + { + int r,_status; + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if(r=nr_ice_component_initialize(ctx,comp)) + ABORT(r); + comp=STAILQ_NEXT(comp,entry); + } + + _status=0; + abort: + return(_status); + } + + +int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp, int *attrctp) + { + int attrct=0; + nr_ice_component *comp; + char **attrs=0; + int index=0; + nr_ice_candidate *cand; + int r,_status; + + *attrctp=0; + + /* First find out how many attributes we need */ + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if (comp->state != NR_ICE_COMPONENT_DISABLED) { + cand = TAILQ_FIRST(&comp->candidates); + while(cand){ + if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) { + ++attrct; + } + + cand = TAILQ_NEXT(cand, entry_comp); + } + } + comp=STAILQ_NEXT(comp,entry); + } + + if(attrct < 1){ + r_log(LOG_ICE,LOG_ERR,"ICE-STREAM(%s): Failed to find any components for stream",stream->label); + ABORT(R_FAILED); + } + + /* Make the array we'll need */ + if(!(attrs=RCALLOC(sizeof(char *)*attrct))) + ABORT(R_NO_MEMORY); + for(index=0;index<attrct;index++){ + if(!(attrs[index]=RMALLOC(NR_ICE_MAX_ATTRIBUTE_SIZE))) + ABORT(R_NO_MEMORY); + } + + index=0; + /* Now format the attributes */ + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if (comp->state != NR_ICE_COMPONENT_DISABLED) { + nr_ice_candidate *cand; + + cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + if (!nr_ice_ctx_hide_candidate(stream->ctx, cand)) { + assert(index < attrct); + + if (index >= attrct) + ABORT(R_INTERNAL); + + if(r=nr_ice_format_candidate_attribute(cand, attrs[index],NR_ICE_MAX_ATTRIBUTE_SIZE)) + ABORT(r); + + index++; + } + + cand=TAILQ_NEXT(cand,entry_comp); + } + } + comp=STAILQ_NEXT(comp,entry); + } + + *attrsp=attrs; + *attrctp=attrct; + + _status=0; + abort: + if(_status){ + if(attrs){ + for(index=0;index<attrct;index++){ + RFREE(attrs[index]); + } + RFREE(attrs); + } + } + return(_status); + } + +/* Get a default candidate per 4.1.4 */ +int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp) + { + int r,_status; + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if (comp->component_id == component) + break; + + comp=STAILQ_NEXT(comp,entry); + } + + if (!comp) + ABORT(R_NOT_FOUND); + + /* If there aren't any IPV4 candidates, try IPV6 */ + if((r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV4)) && + (r=nr_ice_component_get_default_candidate(comp, candp, NR_IPV6))) { + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + + +int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream) + { + int r,_status; + nr_ice_component *pcomp,*lcomp; + + pcomp=STAILQ_FIRST(&pstream->components); + lcomp=STAILQ_FIRST(&lstream->components); + while(pcomp){ + if ((lcomp->state != NR_ICE_COMPONENT_DISABLED) && + (pcomp->state != NR_ICE_COMPONENT_DISABLED)) { + if(r=nr_ice_component_pair_candidates(pctx,lcomp,pcomp)) + ABORT(r); + } + + lcomp=STAILQ_NEXT(lcomp,entry); + pcomp=STAILQ_NEXT(pcomp,entry); + }; + + if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) { + nr_ice_media_stream_set_state(pstream, NR_ICE_MEDIA_STREAM_CHECKS_FROZEN); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, nr_ice_media_stream *pstream, int *serviced) + { + nr_ice_component *pcomp; + int r,_status; + char *user = 0; + + if (serviced) + *serviced = 0; + + pcomp=STAILQ_FIRST(&pstream->components); + while(pcomp){ + int serviced_inner=0; + + /* Flush all the pre-answer requests */ + if(r=nr_ice_component_service_pre_answer_requests(pctx, pcomp, pstream->r2l_user, &serviced_inner)) + ABORT(r); + if (serviced) + *serviced += serviced_inner; + + pcomp=STAILQ_NEXT(pcomp,entry); + } + + _status=0; + abort: + RFREE(user); + return(_status); + } + +/* S 5.8 -- run the first pair from the triggered check queue (even after + * checks have completed S 8.1.2) or run the highest priority WAITING pair or + * if not available FROZEN pair from the check queue */ +static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg) + { + int r,_status; + nr_ice_media_stream *stream=cb_arg; + nr_ice_cand_pair *pair = 0; + int timer_multiplier=stream->pctx->active_streams ? stream->pctx->active_streams : 1; + int timer_val=stream->pctx->ctx->Ta*timer_multiplier; + + assert(timer_val>0); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label); + stream->timer=0; + + /* The trigger check queue has the highest priority */ + pair=TAILQ_FIRST(&stream->trigger_check_queue); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_WAITING){ + /* Remove the pair from he trigger check queue */ + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Removing pair from trigger check queue %s",stream->pctx->label,pair->as_string); + TAILQ_REMOVE(&stream->trigger_check_queue,pair,triggered_check_queue_entry); + break; + } + pair=TAILQ_NEXT(pair,triggered_check_queue_entry); + } + + if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) { + if(!pair){ + /* Find the highest priority WAITING check and move it to RUNNING */ + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_WAITING) + break; + pair=TAILQ_NEXT(pair,check_queue_entry); + } + } + + /* Hmmm... No WAITING. Let's look for FROZEN */ + if(!pair){ + pair=TAILQ_FIRST(&stream->check_list); + + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_FROZEN){ + if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair)) + ABORT(r); + break; + } + pair=TAILQ_NEXT(pair,check_queue_entry); + } + } + } + + if(pair){ + nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */ + NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer); + } + else { + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): no pairs for %s",stream->pctx->label,stream->label); + } + + _status=0; + abort: + return; + } + +/* Start checks for this media stream (aka check list) */ +int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + int r,_status; + + /* Don't start the check timer if the stream is failed */ + if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) { + assert(0); + ABORT(R_INTERNAL); + } + + /* Even if the stream is completed already remote can still create a new + * triggered check request which needs to fire, but not change our stream + * state. */ + if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) { + if(r=nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)) { + ABORT(r); + } + } + + if (!stream->timer) { + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): Starting check timer for stream.",pctx->label,stream->label); + nr_ice_media_stream_check_timer_cb(0,0,stream); + } + + nr_ice_peer_ctx_stream_started_checks(pctx, stream); + + _status=0; + abort: + return(_status); + } + +/* Start checks for this media stream (aka check list) S 5.7 */ +int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + int r,_status; + r_assoc *assoc=0; + nr_ice_cand_pair *pair=0; + + /* Already seen assoc */ + if(r=r_assoc_create(&assoc,r_assoc_crc32_hash_compute,5)) + ABORT(r); + + /* S 5.7.4. Set the highest priority pairs in each foundation to WAITING */ + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + void *v; + + if(r=r_assoc_fetch(assoc,pair->foundation,strlen(pair->foundation),&v)){ + if(r!=R_NOT_FOUND) + ABORT(r); + if(r=nr_ice_candidate_pair_unfreeze(pctx,pair)) + ABORT(r); + + if(r=r_assoc_insert(assoc,pair->foundation,strlen(pair->foundation), + 0,0,0,R_ASSOC_NEW)) + ABORT(r); + } + + /* Already exists... fall through */ + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + _status=0; + abort: + r_assoc_destroy(&assoc); + return(_status); + } + +static int nr_ice_media_stream_unfreeze_pairs_match(nr_ice_media_stream *stream, char *foundation) + { + nr_ice_cand_pair *pair; + int r,_status; + int unfroze=0; + + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_FROZEN && + !strcmp(foundation,pair->foundation)){ + if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair)) + ABORT(r); + unfroze++; + } + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + if(!unfroze) + return(R_NOT_FOUND); + + _status=0; + abort: + return(_status); + } + +/* S 7.1.2.2 */ +int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation) + { + int r,_status; + nr_ice_media_stream *str; + nr_ice_component *comp; + int invalid_comps=0; + + /* 1. Unfreeze all frozen pairs with the same foundation + in this stream */ + if(r=nr_ice_media_stream_unfreeze_pairs_match(stream,foundation)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + + /* 2. See if there is a pair in the valid list for every component */ + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if(!comp->valid_pairs) + invalid_comps++; + + comp=STAILQ_NEXT(comp,entry); + } + + /* If there is a pair in the valid list for every component... */ + /* Now go through the check lists for the other streams */ + str=STAILQ_FIRST(&stream->pctx->peer_streams); + while(str){ + if(str!=stream){ + switch(str->ice_state){ + case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE: + /* Unfreeze matching pairs */ + if(r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation)){ + if(r!=R_NOT_FOUND) + ABORT(r); + } + break; + case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN: + /* Unfreeze matching pairs if any */ + r=nr_ice_media_stream_unfreeze_pairs_match(str,foundation); + if(r){ + if(r!=R_NOT_FOUND) + ABORT(r); + + /* OK, no matching pairs: execute the algorithm from 5.7 + for this stream */ + if(r=nr_ice_media_stream_unfreeze_pairs(str->pctx,str)) + ABORT(r); + } + if(r=nr_ice_media_stream_start_checks(str->pctx,str)) + ABORT(r); + + break; + default: + break; + } + } + + str=STAILQ_NEXT(str,entry); + } + +/* nr_ice_media_stream_dump_state(stream->pctx,stream,stderr); */ + + + _status=0; + abort: + return(_status); + } + + +int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out) + { + nr_ice_cand_pair *pair; + + /* r_log(LOG_ICE,LOG_DEBUG,"MEDIA-STREAM(%s): state dump", stream->label); */ + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + nr_ice_candidate_pair_dump_state(pair,out); + + pair=TAILQ_NEXT(pair,check_queue_entry); + } + + return(0); + } + +int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state) + { + /* Make no-change a no-op */ + if (state == str->ice_state) + return 0; + + assert(state < sizeof(nr_ice_media_stream_states)/sizeof(char *)); + assert(str->ice_state < sizeof(nr_ice_media_stream_states)/sizeof(char *)); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): stream %s state %s->%s", + str->pctx->label,str->label, + nr_ice_media_stream_states[str->ice_state], + nr_ice_media_stream_states[state]); + + if(state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE) + str->pctx->active_streams++; + if(str->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE) + str->pctx->active_streams--; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): %d active streams", + str->pctx->label, str->pctx->active_streams); + + str->ice_state=state; + + return(0); + } + +/* S OK, this component has a nominated. If every component has a nominated, + the stream is ready */ +int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component) + { + int r,_status; + nr_ice_component *comp; + + comp=STAILQ_FIRST(&stream->components); + while(comp){ + if((comp->state != NR_ICE_COMPONENT_DISABLED) && + (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) && + !comp->nominated) + break; + + comp=STAILQ_NEXT(comp,entry); + } + + /* At least one un-nominated component */ + if(comp) + goto done; + + /* All done... */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all active components have nominated candidate pairs",stream->pctx->label,stream->label); + nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED); + + /* Cancel our timer */ + if(stream->timer){ + NR_async_timer_cancel(stream->timer); + stream->timer=0; + } + + if (stream->pctx->handler) { + stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream); + } + + /* Now tell the peer_ctx that we're connected */ + if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx)) + ABORT(r); + + done: + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component) + { + int r,_status; + nr_ice_cand_pair *p2; + + component->state=NR_ICE_COMPONENT_FAILED; + + /* at least one component failed in this media stream, so the entire + * media stream is marked failed */ + + nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_FAILED); + + /* OK, we need to cancel off everything on this component */ + p2=TAILQ_FIRST(&stream->check_list); + while(p2){ + if(r=nr_ice_candidate_pair_cancel(p2->pctx,p2,0)) + ABORT(r); + + p2=TAILQ_NEXT(p2,check_queue_entry); + } + + /* Cancel our timer */ + if(stream->timer){ + NR_async_timer_cancel(stream->timer); + stream->timer=0; + } + + if (stream->pctx->handler) { + stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream); + } + + /* Now tell the peer_ctx that we're connected */ + if(r=nr_ice_peer_ctx_check_if_connected(stream->pctx)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp) + { + nr_ice_candidate *cand; + nr_ice_candidate *best_cand=0; + nr_ice_component *comp; + int r,_status; + + if(r=nr_ice_media_stream_find_component(str,component,&comp)) + ABORT(r); + + cand=TAILQ_FIRST(&comp->candidates); + while(cand){ + if(cand->state==NR_ICE_CAND_STATE_INITIALIZED){ + if(!best_cand || (cand->priority>best_cand->priority)) + best_cand=cand; + + } + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!best_cand) + ABORT(R_NOT_FOUND); + + *candp=best_cand; + + _status=0; + abort: + return(_status); + } + + +/* OK, we have the stream the user created, but that reflects the base + ICE ctx, not the peer_ctx. So, find the related stream in the pctx, + and then find the component */ +int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp) + { + int _status; + nr_ice_component *comp; + + comp=STAILQ_FIRST(&str->components); + while(comp){ + if(comp->component_id==comp_id) + break; + + comp=STAILQ_NEXT(comp,entry); + } + if(!comp) + ABORT(R_NOT_FOUND); + + *compp=comp; + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len) + { + int r,_status; + nr_ice_component *comp; + + /* First find the peer component */ + if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp)) + ABORT(r); + + /* Do we have an active pair yet? We should... */ + if(!comp->active) + ABORT(R_NOT_FOUND); + + /* Does fresh ICE consent exist? */ + if(!comp->can_send) + ABORT(R_FAILED); + + /* OK, write to that pair, which means: + 1. Use the socket on our local side. + 2. Use the address on the remote side + */ + if(r=nr_socket_sendto(comp->active->local->osock,data,len,0, + &comp->active->remote->addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +/* Returns R_REJECTED if the component is unpaired or has been disabled. */ +int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote) + { + int r,_status; + nr_ice_component *comp; + + /* First find the peer component */ + if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp)) + ABORT(r); + + if (comp->state == NR_ICE_COMPONENT_UNPAIRED || + comp->state == NR_ICE_COMPONENT_DISABLED) + ABORT(R_REJECTED); + + if(!comp->active) + ABORT(R_NOT_FOUND); + + if (local) *local = comp->active->local; + if (remote) *remote = comp->active->remote; + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote) + { + int r,_status; + nr_ice_component *comp; + + /* First find the peer component */ + if(r=nr_ice_peer_ctx_find_component(pctx, str, component, &comp)) + ABORT(r); + + /* Do we have an active pair yet? We should... */ + if(!comp->active) + ABORT(R_BAD_ARGS); + + /* Use the socket on our local side */ + if(r=nr_socket_getaddr(comp->active->local->osock,local)) + ABORT(r); + + /* Use the address on the remote side */ + if(r=nr_transport_addr_copy(remote,&comp->active->remote->addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + + + +int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr) + { + nr_ice_component *lcomp,*rcomp; + + r_log(LOG_ICE,LOG_DEBUG,"Finalizing media stream %s, peer=%s",lstr->label, + rstr?rstr->label:"NONE"); + + lcomp=STAILQ_FIRST(&lstr->components); + if(rstr) + rcomp=STAILQ_FIRST(&rstr->components); + else + rcomp=0; + + while(lcomp){ + nr_ice_component_finalize(lcomp,rcomp); + + lcomp=STAILQ_NEXT(lcomp,entry); + if(rcomp){ + rcomp=STAILQ_NEXT(rcomp,entry); + } + } + + return(0); + } + +int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand) + { + int r,_status; + nr_ice_component *comp; + + if ((r=nr_ice_media_stream_find_component(pstream, cand->component_id, &comp))) + ABORT(R_NOT_FOUND); + + if (r=nr_ice_component_pair_candidate(pctx, comp, cand, 1)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int +component_id, int *can_send, struct timeval *ts) + { + int r,_status; + nr_ice_component *comp; + + if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp))) + ABORT(r); + + *can_send = comp->can_send; + ts->tv_sec = comp->consent_last_seen.tv_sec; + ts->tv_usec = comp->consent_last_seen.tv_usec; + _status=0; + abort: + return(_status); + } + +int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id) + { + int r,_status; + nr_ice_component *comp; + + if (stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED) + ABORT(R_FAILED); + + if ((r=nr_ice_media_stream_find_component(stream, component_id, &comp))) + ABORT(r); + + /* Can only disable before pairing */ + if (comp->state != NR_ICE_COMPONENT_UNPAIRED && + comp->state != NR_ICE_COMPONENT_DISABLED) + ABORT(R_FAILED); + + comp->state = NR_ICE_COMPONENT_DISABLED; + + _status=0; + abort: + return(_status); + } + +void nr_ice_media_stream_role_change(nr_ice_media_stream *stream) + { + nr_ice_cand_pair *pair,*temp_pair; + /* Changing role causes candidate pair priority to change, which requires + * re-sorting the check list. */ + nr_ice_cand_pair_head old_checklist; + + assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED); + + /* Move check_list to old_checklist (not POD, have to do the hard way) */ + TAILQ_INIT(&old_checklist); + TAILQ_FOREACH_SAFE(pair,&stream->check_list,check_queue_entry,temp_pair) { + TAILQ_REMOVE(&stream->check_list,pair,check_queue_entry); + TAILQ_INSERT_TAIL(&old_checklist,pair,check_queue_entry); + } + + /* Re-insert into the check list */ + TAILQ_FOREACH_SAFE(pair,&old_checklist,check_queue_entry,temp_pair) { + TAILQ_REMOVE(&old_checklist,pair,check_queue_entry); + nr_ice_candidate_pair_role_change(pair); + nr_ice_candidate_pair_insert(&stream->check_list,pair); + } + } + +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *lcand, nr_ice_candidate *rcand, nr_ice_cand_pair **pair) + { + nr_ice_cand_pair_head *head = &str->check_list; + nr_ice_cand_pair *c1; + + c1=TAILQ_FIRST(head); + while(c1){ + if(c1->local == lcand && + c1->remote == rcand) { + *pair=c1; + return(0); + } + + c1=TAILQ_NEXT(c1,check_queue_entry); + } + + return(R_NOT_FOUND); + } diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h new file mode 100644 index 000000000..3d818d530 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h @@ -0,0 +1,108 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_media_stream_h +#define _ice_media_stream_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +struct nr_ice_media_stream_ { + char *label; + struct nr_ice_ctx_ *ctx; + struct nr_ice_peer_ctx_ *pctx; + + struct nr_ice_media_stream_ *local_stream; /* used when this is a peer */ + int component_ct; + nr_ice_component_head components; + + char *ufrag; /* ICE username */ + char *pwd; /* ICE password */ + char *r2l_user; /* The username for incoming requests */ + char *l2r_user; /* The username for outgoing requests */ + Data r2l_pass; /* The password for incoming requests */ + Data l2r_pass; /* The password for outcoming requests */ + int ice_state; + +#define NR_ICE_MEDIA_STREAM_UNPAIRED 1 +#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2 +#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 3 +#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 4 +#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5 + + nr_ice_cand_pair_head check_list; + nr_ice_cand_pair_head trigger_check_queue; + void *timer; /* Check list periodic timer */ + +/* nr_ice_cand_pair_head valid_list; */ + + STAILQ_ENTRY(nr_ice_media_stream_) entry; +}; + +typedef STAILQ_HEAD(nr_ice_media_stream_head_,nr_ice_media_stream_) nr_ice_media_stream_head; + +int nr_ice_media_stream_create(struct nr_ice_ctx_ *ctx,char *label, int components, nr_ice_media_stream **streamp); +int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp); +int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr); +int nr_ice_media_stream_initialize(struct nr_ice_ctx_ *ctx, nr_ice_media_stream *stream); +int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp,int *attrctp); +int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp); +int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream); +int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); +int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream, int *serviced); +int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); +int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation); +int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out); +int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component); +int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component); +int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); +int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp); +int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len); +int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote); +int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp); +int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *local, nr_ice_candidate *remote, nr_ice_cand_pair **pair); +int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote); +int +nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr); +int nr_ice_media_stream_get_consent_status(nr_ice_media_stream *stream, int component_id, int *can_send, struct timeval *ts); +int nr_ice_media_stream_disable_component(nr_ice_media_stream *stream, int component_id); +int nr_ice_media_stream_pair_new_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, nr_ice_candidate *cand); +void nr_ice_media_stream_role_change(nr_ice_media_stream *stream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_parser.c b/media/mtransport/third_party/nICEr/src/ice/ice_parser.c new file mode 100644 index 000000000..7ec2bec5b --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_parser.c @@ -0,0 +1,575 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_parser.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <csi_platform.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <strings.h> +#endif +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include "nr_api.h" +#include "ice_ctx.h" +#include "ice_candidate.h" +#include "ice_reg.h" + +static void +skip_whitespace(char **str) +{ + char *c = *str; + while (*c == ' ') + ++c; + + *str = c; +} + +static void +fast_forward(char **str, int skip) +{ + char *c = *str; + while (*c != '\0' && skip-- > 0) + ++c; + + *str = c; +} + +static void +skip_to_past_space(char **str) +{ + char *c = *str; + while (*c != ' ' && *c != '\0') + ++c; + + *str = c; + + skip_whitespace(str); +} + +static int +grab_token(char **str, char **out) +{ + int _status; + char *c = *str; + int len; + char *tmp; + + while (*c != ' ' && *c != '\0') + ++c; + + len = c - *str; + + tmp = RMALLOC(len + 1); + if (!tmp) + ABORT(R_NO_MEMORY); + + memcpy(tmp, *str, len); + tmp[len] = '\0'; + + *str = c; + *out = tmp; + + _status = 0; +abort: + return _status; +} + +int +nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *orig,nr_ice_media_stream *stream,nr_ice_candidate **candp) +{ + int r,_status; + char* str = orig; + nr_ice_candidate *cand; + char *connection_address=0; + unsigned int port; + int i; + unsigned int component_id; + char *rel_addr=0; + unsigned char transport; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + + if(!(cand->label=r_strdup(orig))) + ABORT(R_NO_MEMORY); + + cand->ctx=ctx; + cand->isock=0; + cand->state=NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED; + cand->stream=stream; + skip_whitespace(&str); + + /* Skip a= if present */ + if (!strncmp(str, "a=", 2)) + str += 2; + + /* Candidate attr */ + if (strncasecmp(str, "candidate:", 10)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Foundation */ + if ((r=grab_token(&str, &cand->foundation))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* component */ + if (sscanf(str, "%u", &component_id) != 1) + ABORT(R_BAD_DATA); + + if (component_id < 1 || component_id > 256) + ABORT(R_BAD_DATA); + + cand->component_id = (UCHAR)component_id; + + skip_to_past_space(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Protocol */ + if (!strncasecmp(str, "UDP", 3)) + transport=IPPROTO_UDP; + else if (!strncasecmp(str, "TCP", 3)) + transport=IPPROTO_TCP; + else + ABORT(R_BAD_DATA); + + fast_forward(&str, 3); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* priority */ + if (sscanf(str, "%u", &cand->priority) != 1) + ABORT(R_BAD_DATA); + + if (cand->priority < 1) + ABORT(R_BAD_DATA); + + skip_to_past_space(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Peer address/port */ + if ((r=grab_token(&str, &connection_address))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (sscanf(str, "%u", &port) != 1) + ABORT(R_BAD_DATA); + + if (port < 1 || port > 0x0FFFF) + ABORT(R_BAD_DATA); + + if ((r=nr_str_port_to_transport_addr(connection_address,port,transport,&cand->addr))) + ABORT(r); + + skip_to_past_space(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + /* Type */ + if (strncasecmp("typ", str, 3)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 3); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + assert(nr_ice_candidate_type_names[0] == 0); + + for (i = 1; nr_ice_candidate_type_names[i]; ++i) { + if(!strncasecmp(nr_ice_candidate_type_names[i], str, strlen(nr_ice_candidate_type_names[i]))) { + cand->type=i; + break; + } + } + if (nr_ice_candidate_type_names[i] == 0) + ABORT(R_BAD_DATA); + + fast_forward(&str, strlen(nr_ice_candidate_type_names[i])); + + /* Look for the other side's raddr, rport */ + /* raddr, rport */ + switch (cand->type) { + case HOST: + break; + case SERVER_REFLEXIVE: + case PEER_REFLEXIVE: + case RELAYED: + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (strncasecmp("raddr", str, 5)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 5); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if ((r=grab_token(&str, &rel_addr))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (strncasecmp("rport", str, 5)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 5); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (sscanf(str, "%u", &port) != 1) + ABORT(R_BAD_DATA); + + if (port > 0x0FFFF) + ABORT(R_BAD_DATA); + + if ((r=nr_str_port_to_transport_addr(rel_addr,port,transport,&cand->base))) + ABORT(r); + + skip_to_past_space(&str); + /* it's expected to be at EOD at this point */ + + break; + default: + ABORT(R_INTERNAL); + break; + } + + skip_whitespace(&str); + + if (transport == IPPROTO_TCP && cand->type != RELAYED) { + /* Parse tcptype extension per RFC 6544 S 4.5 */ + if (strncasecmp("tcptype ", str, 8)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 8); + skip_whitespace(&str); + + for (i = 1; nr_ice_candidate_tcp_type_names[i]; ++i) { + if(!strncasecmp(nr_ice_candidate_tcp_type_names[i], str, strlen(nr_ice_candidate_tcp_type_names[i]))) { + cand->tcp_type=i; + fast_forward(&str, strlen(nr_ice_candidate_tcp_type_names[i])); + break; + } + } + + if (cand->tcp_type == 0) + ABORT(R_BAD_DATA); + + if (*str && *str != ' ') + ABORT(R_BAD_DATA); + } + /* Ignore extensions per RFC 5245 S 15.1 */ +#if 0 + /* This used to be an assert, but we don't want to exit on invalid + remote data */ + if (strlen(str) != 0) { + ABORT(R_BAD_DATA); + } +#endif + + nr_ice_candidate_compute_codeword(cand); + + *candp=cand; + + _status=0; + abort: + if (_status){ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error parsing attribute: %s",ctx->label,orig); + nr_ice_candidate_destroy(&cand); + } + + RFREE(connection_address); + RFREE(rel_addr); + return(_status); +} + + +int +nr_ice_peer_ctx_parse_media_stream_attribute(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *attr) +{ + int r,_status; + char *orig = 0; + char *str; + + orig = str = attr; + + if (!strncasecmp(str, "ice-ufrag:", 10)) { + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if ((r=grab_token(&str, &stream->ufrag))) + ABORT(r); + } + else if (!strncasecmp(str, "ice-pwd:", 8)) { + fast_forward(&str, 8); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if ((r=grab_token(&str, &stream->pwd))) + ABORT(r); + } + else { + ABORT(R_BAD_DATA); + } + + skip_whitespace(&str); + + /* RFC 5245 grammar doesn't have an extension point for ice-pwd or + ice-ufrag: if there's anything left on the line, we treat it as bad. */ + if (str[0] != '\0') { + ABORT(R_BAD_DATA); + } + + _status=0; + abort: + if (_status) { + if (orig) + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig); + } + + return(_status); +} + +int +nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct) +{ + int r,_status; + int i; + char *orig = 0; + char *str; + char *component_id = 0; + char *connection_address = 0; + unsigned int port; + in_addr_t addr; + char *ice_option_tag = 0; + + for(i=0;i<attr_ct;i++){ + orig = str = attrs[i]; + + component_id = 0; + connection_address = 0; + ice_option_tag = 0; + + if (!strncasecmp(str, "remote-candidates:", 18)) { + fast_forward(&str, 18); + skip_whitespace(&str); + + while (*str != '\0') { + if ((r=grab_token(&str, &component_id))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if ((r=grab_token(&str, &connection_address))) + ABORT(r); + + if (*str == '\0') + ABORT(R_BAD_DATA); + + addr = inet_addr(connection_address); + if (addr == INADDR_NONE) + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + if (sscanf(str, "%u", &port) != 1) + ABORT(R_BAD_DATA); + + if (port < 1 || port > 0x0FFFF) + ABORT(R_BAD_DATA); + + skip_to_past_space(&str); + +#if 0 + /* TODO: !nn! just drop on the floor for now, later put somewhere */ + /* Assume v4 for now */ + if(r=nr_ip4_port_to_transport_addr(ntohl(addr),port,IPPROTO_UDP,&candidate->base)) + ABORT(r); + + TAILQ_INSERT_TAIL(head, elm, field); +#endif + + component_id = 0; /* prevent free */ + RFREE(connection_address); + connection_address = 0; /* prevent free */ + } + } + else if (!strncasecmp(str, "ice-lite", 8)) { + pctx->peer_lite = 1; + + fast_forward(&str, 8); + } + else if (!strncasecmp(str, "ice-mismatch", 12)) { + pctx->peer_ice_mismatch = 1; + + fast_forward(&str, 12); + } + else if (!strncasecmp(str, "ice-ufrag:", 10)) { + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + RFREE(pctx->peer_ufrag); + pctx->peer_ufrag = 0; + if ((r=grab_token(&str, &pctx->peer_ufrag))) + ABORT(r); + } + else if (!strncasecmp(str, "ice-pwd:", 8)) { + fast_forward(&str, 8); + if (*str == '\0') + ABORT(R_BAD_DATA); + + skip_whitespace(&str); + if (*str == '\0') + ABORT(R_BAD_DATA); + + RFREE(pctx->peer_pwd); + pctx->peer_pwd = 0; + if ((r=grab_token(&str, &pctx->peer_pwd))) + ABORT(r); + } + else if (!strncasecmp(str, "ice-options:", 12)) { + fast_forward(&str, 12); + skip_whitespace(&str); + + while (*str != '\0') { + if ((r=grab_token(&str, &ice_option_tag))) + ABORT(r); + + skip_whitespace(&str); + + //TODO: for now, just throw away; later put somewhere + RFREE(ice_option_tag); + + ice_option_tag = 0; /* prevent free */ + } + } + else { + ABORT(R_BAD_DATA); + } + + skip_whitespace(&str); + + /* RFC 5245 grammar doesn't have an extension point for any of the + preceding attributes: if there's anything left on the line, we + treat it as bad data. */ + if (str[0] != '\0') { + ABORT(R_BAD_DATA); + } + } + + _status=0; + abort: + if (_status) { + if (orig) + r_log(LOG_ICE,LOG_WARNING,"ICE-PEER(%s): Error parsing attribute: %s",pctx->label,orig); + } + + RFREE(connection_address); + RFREE(component_id); + RFREE(ice_option_tag); + return(_status); +} + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c new file mode 100644 index 000000000..d494e45de --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c @@ -0,0 +1,825 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_peer_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <string.h> +#include <assert.h> +#include <registry.h> +#include <nr_api.h> +#include "ice_ctx.h" +#include "ice_peer_ctx.h" +#include "ice_media_stream.h" +#include "ice_util.h" +#include "nr_crypto.h" +#include "async_timer.h" +#include "ice_reg.h" + +static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg); +static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct); +static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate); + +int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp) + { + int r,_status; + nr_ice_peer_ctx *pctx=0; + + if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx)))) + ABORT(R_NO_MEMORY); + + pctx->state = NR_ICE_PEER_STATE_UNPAIRED; + + if(!(pctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + pctx->ctx=ctx; + pctx->handler=handler; + + /* Decide controlling vs. controlled */ + if(ctx->flags & NR_ICE_CTX_FLAGS_LITE){ + pctx->controlling=0; + } + else{ + if(ctx->flags & NR_ICE_CTX_FLAGS_OFFERER) + pctx->controlling=1; + else if(ctx->flags & NR_ICE_CTX_FLAGS_ANSWERER) + pctx->controlling=0; + } + if(r=nr_crypto_random_bytes((UCHAR *)&pctx->tiebreaker,8)) + ABORT(r); + + STAILQ_INIT(&pctx->peer_streams); + + STAILQ_INSERT_TAIL(&ctx->peers,pctx,entry); + + *pctxp=pctx; + + _status = 0; + abort: + if(_status){ + nr_ice_peer_ctx_destroy_cb(0,0,pctx); + } + return(_status); + } + + + +int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct) + { + nr_ice_media_stream *pstream=0; + nr_ice_component *comp,*comp2; + char *lufrag,*rufrag; + char *lpwd,*rpwd; + int r,_status; + + /* + Note: use component_ct from our own stream since components other + than this offered by the other side are unusable */ + if(r=nr_ice_media_stream_create(pctx->ctx,stream->label,stream->component_ct,&pstream)) + ABORT(r); + + /* Match up the local and remote components */ + comp=STAILQ_FIRST(&stream->components); + comp2=STAILQ_FIRST(&pstream->components); + while(comp){ + comp2->local_component=comp; + + comp=STAILQ_NEXT(comp,entry); + comp2=STAILQ_NEXT(comp2,entry); + } + + pstream->local_stream=stream; + pstream->pctx=pctx; + + if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct)) + ABORT(r); + + /* Now that we have the ufrag and password, compute all the username/password + pairs */ + lufrag=stream->ufrag?stream->ufrag:pctx->ctx->ufrag; + lpwd=stream->pwd?stream->pwd:pctx->ctx->pwd; + assert(lufrag); + assert(lpwd); + rufrag=pstream->ufrag?pstream->ufrag:pctx->peer_ufrag; + rpwd=pstream->pwd?pstream->pwd:pctx->peer_pwd; + if (!rufrag || !rpwd) + ABORT(R_BAD_DATA); + + if(r=nr_concat_strings(&pstream->r2l_user,lufrag,":",rufrag,NULL)) + ABORT(r); + if(r=nr_concat_strings(&pstream->l2r_user,rufrag,":",lufrag,NULL)) + ABORT(r); + if(r=r_data_make(&pstream->r2l_pass, (UCHAR *)lpwd, strlen(lpwd))) + ABORT(r); + if(r=r_data_make(&pstream->l2r_pass, (UCHAR *)rpwd, strlen(rpwd))) + ABORT(r); + + STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry); + + _status=0; + abort: + return(_status); + } + +static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct) + { + int r; + int i; + + for(i=0;i<attr_ct;i++){ + if(!strncmp(attrs[i],"ice-",4)){ + if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus ICE attribute",pctx->ctx->label,pctx->label); + continue; + } + } + else if (!strncmp(attrs[i],"candidate",9)){ + if(r=nr_ice_ctx_parse_candidate(pctx,pstream,attrs[i])) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus candidate",pctx->ctx->label,pctx->label); + continue; + } + } + else { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) specified bogus attribute: %s",pctx->ctx->label,pctx->label,attrs[i]); + } + } + + /* Doesn't fail because we just skip errors */ + return(0); + } + +static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate) + { + nr_ice_candidate *cand=0; + nr_ice_component *comp; + int j; + int r, _status; + + if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,candidate,pstream,&cand)) + ABORT(r); + if(cand->component_id-1>=pstream->component_ct){ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label); + ABORT(R_BAD_DATA); + } + + /* Not the fastest way to find a component, but it's what we got */ + j=1; + for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){ + if(j==cand->component_id) + break; + + j++; + } + + if(!comp){ + r_log(LOG_ICE,LOG_WARNING,"Peer answered with more components than we offered"); + ABORT(R_BAD_DATA); + } + + if (comp->state == NR_ICE_COMPONENT_DISABLED) { + r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled remote component"); + ABORT(R_BAD_DATA); + } + if (comp->local_component->state == NR_ICE_COMPONENT_DISABLED) { + r_log(LOG_ICE,LOG_WARNING,"Peer offered candidates for disabled local component"); + ABORT(R_BAD_DATA); + } + + cand->component=comp; + + TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/CAND(%s): creating peer candidate", + pctx->label,cand->label); + + _status=0; + abort: + if (_status) { + nr_ice_candidate_destroy(&cand); + } + return(_status); + } + +int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp) + { + int _status; + nr_ice_media_stream *pstream; + + /* Because we don't have forward pointers, iterate through all the + peer streams to find one that matches us */ + pstream=STAILQ_FIRST(&pctx->peer_streams); + while(pstream) { + if (pstream->local_stream == stream) + break; + + pstream = STAILQ_NEXT(pstream, entry); + } + if (!pstream) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label); + ABORT(R_NOT_FOUND); + } + + *pstreamp = pstream; + + _status=0; + abort: + return(_status); + } + +int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp) + { + int r,_status; + + STAILQ_REMOVE(&pctx->peer_streams,*pstreamp,nr_ice_media_stream_,entry); + + if(r=nr_ice_media_stream_destroy(pstreamp)) { + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate) + { + nr_ice_media_stream *pstream; + int r,_status; + int needs_pairing = 0; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) parsing trickle ICE candidate %s",pctx->ctx->label,pctx->label,candidate); + r = nr_ice_peer_ctx_find_pstream(pctx, stream, &pstream); + if (r) + ABORT(r); + + switch(pstream->ice_state) { + case NR_ICE_MEDIA_STREAM_UNPAIRED: + break; + case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN: + case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE: + needs_pairing = 1; + break; + default: + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) tried to trickle ICE in inappropriate state %d",pctx->ctx->label,pctx->label,stream->label,pstream->ice_state); + ABORT(R_ALREADY); + break; + } + + if(r=nr_ice_ctx_parse_candidate(pctx,pstream,candidate)){ + ABORT(r); + } + + /* If ICE is running (i.e., we are in FROZEN or ACTIVE states) + then we need to pair this new candidate. For now we + just re-pair the stream which is inefficient but still + fine because we suppress duplicate pairing */ + if (needs_pairing) { + if(r=nr_ice_media_stream_pair_candidates(pctx, stream, pstream)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to pair trickle ICE candidates",pctx->ctx->label,pctx->label,stream->label); + ABORT(r); + } + + /* Start checks if this stream is not checking yet or if it has checked + all the available candidates but not had a completed check for all + components. + + Note that this is not compliant with RFC 5245, but consistent with + the libjingle trickle ICE behavior. Note that we will not restart + checks if either (a) the stream has failed or (b) all components + have a successful pair because the switch statement above jumps + will in both states. + + TODO(ekr@rtfm.com): restart checks. + TODO(ekr@rtfm.com): update when the trickle ICE RFC is published + */ + if (!pstream->timer) { + if(r=nr_ice_media_stream_start_checks(pctx, pstream)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to start checks",pctx->ctx->label,pctx->label,stream->label); + ABORT(r); + } + } + } + + _status=0; + abort: + return(_status); + + } + + +static void nr_ice_peer_ctx_trickle_wait_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + nr_ice_media_stream *stream; + nr_ice_component *comp; + + pctx->trickle_grace_period_timer=0; + + r_log(LOG_ICE,LOG_INFO,"ICE(%s): peer (%s) Trickle grace period is over; marking every component with only failed pairs as failed.",pctx->ctx->label,pctx->label); + + stream=STAILQ_FIRST(&pctx->peer_streams); + while(stream){ + comp=STAILQ_FIRST(&stream->components); + while(comp){ + nr_ice_component_check_if_failed(comp); + comp=STAILQ_NEXT(comp,entry); + } + stream=STAILQ_NEXT(stream,entry); + } + } + +static void nr_ice_peer_ctx_start_trickle_timer(nr_ice_peer_ctx *pctx) + { + UINT4 grace_period_timeout=0; + + if(pctx->trickle_grace_period_timer) { + NR_async_timer_cancel(pctx->trickle_grace_period_timer); + pctx->trickle_grace_period_timer=0; + } + + NR_reg_get_uint4(NR_ICE_REG_TRICKLE_GRACE_PERIOD,&grace_period_timeout); + + if (grace_period_timeout) { + /* If we're doing trickle, we need to allow a grace period for new + * trickle candidates to arrive in case the pairs we have fail quickly. */ + NR_ASYNC_TIMER_SET(grace_period_timeout,nr_ice_peer_ctx_trickle_wait_cb,pctx,&pctx->trickle_grace_period_timer); + } + } + +int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *stream; + int r,_status; + + if(pctx->peer_lite && !pctx->controlling) { + if(pctx->ctx->flags & NR_ICE_CTX_FLAGS_LITE){ + r_log(LOG_ICE,LOG_ERR,"Both sides are ICE-Lite"); + ABORT(R_BAD_DATA); + } + nr_ice_peer_ctx_switch_controlling_role(pctx); + } + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): peer (%s) pairing candidates",pctx->ctx->label,pctx->label); + + if(STAILQ_EMPTY(&pctx->peer_streams)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attributes",pctx->ctx->label,pctx->label); + ABORT(R_FAILED); + } + + /* Set this first; if we fail partway through, we do not want to end + * up in UNPAIRED after creating some pairs. */ + pctx->state = NR_ICE_PEER_STATE_PAIRED; + + /* Start grace period timer for incoming trickle candidates */ + nr_ice_peer_ctx_start_trickle_timer(pctx); + + stream=STAILQ_FIRST(&pctx->peer_streams); + while(stream){ + if(r=nr_ice_media_stream_pair_candidates(pctx, stream->local_stream, + stream)) + ABORT(r); + + stream=STAILQ_NEXT(stream,entry); + } + + + _status=0; + abort: + return(_status); + } + + +int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand) + { + int r, _status; + nr_ice_media_stream *pstream; + + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) pairing local trickle ICE candidate %s",pctx->ctx->label,pctx->label,cand->label); + if ((r = nr_ice_peer_ctx_find_pstream(pctx, cand->stream, &pstream))) + ABORT(r); + + if ((r = nr_ice_media_stream_pair_new_trickle_candidate(pctx, pstream, cand))) + ABORT(r); + + _status=0; + abort: + return _status; + } + +int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id) + { + int r, _status; + nr_ice_media_stream *pstream; + nr_ice_component *component; + + if ((r=nr_ice_peer_ctx_find_pstream(pctx, lstream, &pstream))) + ABORT(r); + + /* We shouldn't be calling this after we have started pairing */ + if (pstream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED) + ABORT(R_FAILED); + + if ((r=nr_ice_media_stream_find_component(pstream, component_id, + &component))) + ABORT(r); + + component->state = NR_ICE_COMPONENT_DISABLED; + + _status=0; + abort: + return(_status); + } + +static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + nr_ice_media_stream *str1,*str2; + + NR_async_timer_cancel(pctx->connected_cb_timer); + RFREE(pctx->label); + RFREE(pctx->peer_ufrag); + RFREE(pctx->peer_pwd); + + STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){ + STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry); + nr_ice_media_stream_destroy(&str1); + } + assert(pctx->ctx); + if (pctx->ctx) + STAILQ_REMOVE(&pctx->ctx->peers, pctx, nr_ice_peer_ctx_, entry); + + if(pctx->trickle_grace_period_timer) { + NR_async_timer_cancel(pctx->trickle_grace_period_timer); + pctx->trickle_grace_period_timer=0; + } + + RFREE(pctx); + } + +int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp) + { + + if(!pctxp || !*pctxp) + return(0); + + /* Stop calling the handler */ + (*pctxp)->handler = 0; + + NR_ASYNC_SCHEDULE(nr_ice_peer_ctx_destroy_cb,*pctxp); + + *pctxp=0; + + return(0); + } + + +/* Start the checks for the first media stream (S 5.7) + The rest remain FROZEN */ +int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx) + { + return nr_ice_peer_ctx_start_checks2(pctx, 0); + } + +/* Start checks for some media stream. + + If allow_non_first == 0, then we only look at the first stream, + which is 5245-complaint. + + If allow_non_first == 1 then we find the first non-empty stream + This is not compliant with RFC 5245 but is necessary to make trickle ICE + work plausibly +*/ +int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first) + { + int r,_status; + nr_ice_media_stream *stream; + int started = 0; + + /* Might have added some streams */ + pctx->reported_connected = 0; + NR_async_timer_cancel(pctx->connected_cb_timer); + pctx->connected_cb_timer = 0; + pctx->checks_started = 0; + + if((r=nr_ice_peer_ctx_check_if_connected(pctx))) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) initial connected check failed",pctx->ctx->label,pctx->label); + ABORT(r); + } + + if (pctx->reported_connected) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) in %s all streams were done",pctx->ctx->label,pctx->label,__FUNCTION__); + return (0); + } + + stream=STAILQ_FIRST(&pctx->peer_streams); + if(!stream) + ABORT(R_FAILED); + + while (stream) { + assert(stream->ice_state != NR_ICE_MEDIA_STREAM_UNPAIRED); + + if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) { + if(!TAILQ_EMPTY(&stream->check_list)) + break; + + if(!allow_non_first){ + /* This test applies if: + + 1. allow_non_first is 0 (i.e., non-trickle ICE) + 2. the first stream has an empty check list. + + But in the non-trickle ICE case, the other side should have provided + some candidates or ICE is pretty much not going to work and we're + just going to fail. Hence R_FAILED as opposed to R_NOT_FOUND and + immediate termination here. + */ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label); + ABORT(R_FAILED); + } + } + + stream=STAILQ_NEXT(stream, entry); + } + + if (!stream) { + /* + We fail above if we aren't doing trickle, and this is not all that + unusual in the trickle case. + */ + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label); + } + else if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) { + if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream)) + ABORT(r); + if(r=nr_ice_media_stream_start_checks(pctx,stream)) + ABORT(r); + ++started; + } + + stream=STAILQ_FIRST(&pctx->peer_streams); + while (stream) { + int serviced = 0; + if (r=nr_ice_media_stream_service_pre_answer_requests(pctx, stream->local_stream, stream, &serviced)) + ABORT(r); + + if (serviced) { + ++started; + } + else { + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no streams with pre-answer requests",pctx->ctx->label,pctx->label); + } + + + stream=STAILQ_NEXT(stream, entry); + } + + if (!started) { + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) no checks to start",pctx->ctx->label,pctx->label); + ABORT(R_NOT_FOUND); + } + + _status=0; + abort: + return(_status); + } + +void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + if (!pctx->checks_started) { + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): peer (%s) is now checking",pctx->ctx->label,pctx->label); + pctx->checks_started = 1; + if (pctx->handler && pctx->handler->vtbl->ice_checking) { + pctx->handler->vtbl->ice_checking(pctx->handler->obj, pctx); + } + } + } + +#ifndef NDEBUG +int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out) + { + int r,_status; + nr_ice_media_stream *stream; + + fprintf(out,"PEER %s STATE DUMP\n",pctx->label); + fprintf(out,"==========================================\n"); + stream=STAILQ_FIRST(&pctx->peer_streams); + while(stream){ + if(r=nr_ice_media_stream_dump_state(pctx,stream,out)) + ABORT(r); + + stream=STAILQ_NEXT(stream,entry); + } + fprintf(out,"==========================================\n"); + + _status=0; + abort: + return(_status); + } +#endif + +void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx) + { + if (pctx->reported_connected && + pctx->handler && + pctx->handler->vtbl->ice_disconnected) { + pctx->handler->vtbl->ice_disconnected(pctx->handler->obj, pctx); + } + } + +void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx) + { + /* Fire the handler callback to say we're done */ + if (pctx->reported_connected && + pctx->handler && + pctx->handler->vtbl->ice_connected) { + pctx->handler->vtbl->ice_connected(pctx->handler->obj, pctx); + } + } + +static void nr_ice_peer_ctx_fire_connected(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + + pctx->connected_cb_timer=0; + + nr_ice_peer_ctx_connected(pctx); + } + +/* Examine all the streams to see if we're + maybe miraculously connected */ +int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx) + { + int _status; + nr_ice_media_stream *str; + int failed=0; + int succeeded=0; + + str=STAILQ_FIRST(&pctx->peer_streams); + while(str){ + if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED){ + succeeded++; + } + else if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FAILED){ + failed++; + } + else{ + break; + } + str=STAILQ_NEXT(str,entry); + } + + if(str) + goto done; /* Something isn't done */ + + /* OK, we're finished, one way or another */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): all checks completed success=%d fail=%d",pctx->label,succeeded,failed); + + /* Schedule a connected notification for the first connected event. + IMPORTANT: This is done in a callback because we expect destructors + of various kinds to be fired from here */ + if (!pctx->reported_connected) { + pctx->reported_connected = 1; + assert(!pctx->connected_cb_timer); + NR_ASYNC_TIMER_SET(0,nr_ice_peer_ctx_fire_connected,pctx,&pctx->connected_cb_timer); + } + + done: + _status=0; +// abort: + return(_status); + } + + +/* Given a component in the main ICE ctx, find the relevant component in + the peer_ctx */ +int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp) + { + nr_ice_media_stream *pstr; + int r,_status; + + pstr=STAILQ_FIRST(&pctx->peer_streams); + while(pstr){ + if(pstr->local_stream==str) + break; + + pstr=STAILQ_NEXT(pstr,entry); + } + if(!pstr) + ABORT(R_BAD_ARGS); + + if(r=nr_ice_media_stream_find_component(pstr,component_id,compp)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +/* + This packet may be for us. + + 1. Find the matching peer component + 2. Examine the packet source address to see if it matches + one of the peer candidates. + 3. Fire the relevant callback handler if there is a match + + Return 0 if match, R_REJECTED if no match, other errors + if we can't even find the component or something like that. +*/ + +int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len) + { + nr_ice_component *peer_comp; + nr_ice_candidate *cand; + int r,_status; + + if(r=nr_ice_peer_ctx_find_component(pctx, comp->stream, comp->component_id, + &peer_comp)) + ABORT(r); + + /* OK, we've found the component, now look for matches */ + cand=TAILQ_FIRST(&peer_comp->candidates); + while(cand){ + if(!nr_transport_addr_cmp(source_addr,&cand->addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!cand) + ABORT(R_REJECTED); + + /* OK, there's a match. Call the handler */ + + if (pctx->handler) { + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label); + + pctx->handler->vtbl->msg_recvd(pctx->handler->obj, + pctx,comp->stream,comp->component_id,data,len); + } + + _status=0; + abort: + return(_status); + } + +void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx) + { + int controlling = !(pctx->controlling); + if(pctx->controlling_conflict_resolved) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): peer (%s) %s called more than once; " + "this probably means the peer is confused. Not switching roles.", + pctx->ctx->label,pctx->label,__FUNCTION__); + return; + } + + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): detected " + "role conflict. Switching to %s", + pctx->label, + controlling ? "controlling" : "controlled"); + + pctx->controlling = controlling; + pctx->controlling_conflict_resolved = 1; + + if(pctx->state == NR_ICE_PEER_STATE_PAIRED) { + /* We have formed candidate pairs. We need to inform them. */ + nr_ice_media_stream *pstream=STAILQ_FIRST(&pctx->peer_streams); + while(pstream) { + nr_ice_media_stream_role_change(pstream); + pstream = STAILQ_NEXT(pstream, entry); + } + } + } + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h new file mode 100644 index 000000000..47e3be704 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h @@ -0,0 +1,101 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_peer_ctx_h +#define _ice_peer_ctx_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +struct nr_ice_peer_ctx_ { + int state; +#define NR_ICE_PEER_STATE_UNPAIRED 1 +#define NR_ICE_PEER_STATE_PAIRED 2 + + char *label; + nr_ice_ctx *ctx; + nr_ice_handler *handler; + + UCHAR controlling; /* 1 for controlling, 0 for controlled */ + UCHAR controlling_conflict_resolved; + UINT8 tiebreaker; + + char *peer_ufrag; + char *peer_pwd; + int peer_lite; + int peer_ice_mismatch; + + nr_ice_media_stream_head peer_streams; + int active_streams; + int waiting_pairs; + UCHAR checks_started; + + void *connected_cb_timer; + UCHAR reported_connected; + void *trickle_grace_period_timer; + + STAILQ_ENTRY(nr_ice_peer_ctx_) entry; +}; + +typedef STAILQ_HEAD(nr_ice_peer_ctx_head_, nr_ice_peer_ctx_) nr_ice_peer_ctx_head; + +int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp); +int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp); +int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct); +int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp); +int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp); +int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand); + +int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct); +int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first); +void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); +void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx); +void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out); +int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx); +int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp); +int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); +int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id); +int nr_ice_peer_ctx_pair_new_trickle_candidate(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx, nr_ice_candidate *cand); +void nr_ice_peer_ctx_switch_controlling_role(nr_ice_peer_ctx *pctx); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_reg.h b/media/mtransport/third_party/nICEr/src/ice/ice_reg.h new file mode 100644 index 000000000..dc0f74172 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_reg.h @@ -0,0 +1,79 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_reg_h +#define _ice_reg_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +#define NR_ICE_REG_PREF_TYPE_HOST "ice.pref.type.host" +#define NR_ICE_REG_PREF_TYPE_RELAYED "ice.pref.type.relayed" +#define NR_ICE_REG_PREF_TYPE_SRV_RFLX "ice.pref.type.srv_rflx" +#define NR_ICE_REG_PREF_TYPE_PEER_RFLX "ice.pref.type.peer_rflx" +#define NR_ICE_REG_PREF_TYPE_HOST_TCP "ice.pref.type.host_tcp" +#define NR_ICE_REG_PREF_TYPE_RELAYED_TCP "ice.pref.type.relayed_tcp" +#define NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP "ice.pref.type.srv_rflx_tcp" +#define NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP "ice.pref.type.peer_rflx_tcp" + +#define NR_ICE_REG_PREF_INTERFACE_PRFX "ice.pref.interface" +#define NR_ICE_REG_SUPPRESS_INTERFACE_PRFX "ice.suppress.interface" + +#define NR_ICE_REG_STUN_SRV_PRFX "ice.stun.server" +#define NR_ICE_REG_STUN_SRV_ADDR "addr" +#define NR_ICE_REG_STUN_SRV_PORT "port" + +#define NR_ICE_REG_TURN_SRV_PRFX "ice.turn.server" +#define NR_ICE_REG_TURN_SRV_ADDR "addr" +#define NR_ICE_REG_TURN_SRV_PORT "port" +#define NR_ICE_REG_TURN_SRV_BANDWIDTH "bandwidth" +#define NR_ICE_REG_TURN_SRV_LIFETIME "lifetime" +#define NR_ICE_REG_TURN_SRV_USERNAME "username" +#define NR_ICE_REG_TURN_SRV_PASSWORD "password" + +#define NR_ICE_REG_ICE_TCP_DISABLE "ice.tcp.disable" +#define NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT "ice.tcp.so_sock_count" +#define NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG "ice.tcp.listen_backlog" + +#define NR_ICE_REG_KEEPALIVE_TIMER "ice.keepalive_timer" + +#define NR_ICE_REG_TRICKLE_GRACE_PERIOD "ice.trickle_grace_period" +#define NR_ICE_REG_PREF_FORCE_INTERFACE_NAME "ice.forced_interface_name" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c new file mode 100644 index 000000000..f9251a280 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c @@ -0,0 +1,380 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: ice_socket.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + +#include <assert.h> +#include <string.h> +#include "nr_api.h" +#include "ice_ctx.h" +#include "stun.h" +#include "nr_socket_buffered_stun.h" +#include "nr_socket_multi_tcp.h" + +static void nr_ice_socket_readable_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r; + nr_ice_stun_ctx *sc1,*sc2; + nr_ice_socket *sock=cb_arg; + UCHAR buf[9216]; + char string[256]; + nr_transport_addr addr; + int len; + size_t len_s; + int is_stun; + int is_req; + int is_ind; + int processed_indication=0; + + nr_socket *stun_srv_sock=sock->sock; + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label); + + /* Re-arm first! */ + if (sock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP) { + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): rearming",sock->ctx->label); + NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg); + } + + if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){ + if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) { + /* Report this error upward. Bug 946423 */ + r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error %d on reliable socket(%p). Abandoning.",sock->ctx->label, r, s); + } + return; + } + + /* Deal with the fact that sizeof(int) and sizeof(size_t) may not + be the same */ + if (len_s > (size_t)INT_MAX) + return; + + len = (int)len_s; + +#ifdef USE_TURN + re_process: +#endif /* USE_TURN */ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Read %d bytes %sfrom %s",sock->ctx->label,len,(processed_indication ? "relayed " : ""),addr.as_string); + + /* First question: is this STUN or not? */ + is_stun=nr_is_stun_message(buf,len); + + if(is_stun){ + is_req=nr_is_stun_request_message(buf,len); + is_ind=is_req?0:nr_is_stun_indication_message(buf,len); + + snprintf(string, sizeof(string)-1, "ICE(%s): Message is STUN (%s)",sock->ctx->label, + is_req ? "request" : (is_ind ? "indication" : "other")); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)buf, len); + + + /* We need to offer it to all of our stun contexts + to see who bites */ + sc1=TAILQ_FIRST(&sock->stun_ctxs); + while(sc1){ + sc2=TAILQ_NEXT(sc1,entry); + + r=-1; + switch(sc1->type){ + /* This has been deleted, prune... */ + case NR_ICE_STUN_NONE: + TAILQ_REMOVE(&sock->stun_ctxs,sc1,entry); + RFREE(sc1); + break; + + case NR_ICE_STUN_CLIENT: + if(!(is_req||is_ind)){ + r=nr_stun_client_process_response(sc1->u.client,buf,len,&addr); + } + break; + + case NR_ICE_STUN_SERVER: + if(is_req){ + r=nr_stun_server_process_request(sc1->u.server,stun_srv_sock,(char *)buf,len,&addr,NR_STUN_AUTH_RULE_SHORT_TERM); + } + break; +#ifdef USE_TURN + case NR_ICE_TURN_CLIENT: + /* data indications are ok, so don't ignore those */ + /* Check that this is from the right TURN server address. Else + skip */ + if (nr_transport_addr_cmp( + &sc1->u.turn_client.turn_client->turn_server_addr, + &addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + if(!is_req){ + if(!is_ind) + r=nr_turn_client_process_response(sc1->u.turn_client.turn_client,buf,len,&addr); + else{ + nr_transport_addr n_addr; + size_t n_len; + + if (processed_indication) { + /* Don't allow recursively wrapped indications */ + r_log(LOG_ICE, LOG_WARNING, + "ICE(%s): discarding recursively wrapped indication", + sock->ctx->label); + break; + } + /* This is a bit of a hack. If it's a data indication, strip + off the TURN framing and re-enter. This works because + all STUN processing is on the same physical socket. + We don't care about other kinds of indication */ + r=nr_turn_client_parse_data_indication( + sc1->u.turn_client.turn_client, &addr, + buf, len, buf, &n_len, len, &n_addr); + if(!r){ + r_log(LOG_ICE,LOG_DEBUG,"Unwrapped a data indication."); + len=n_len; + nr_transport_addr_copy(&addr,&n_addr); + stun_srv_sock=sc1->u.turn_client.turn_sock; + processed_indication=1; + goto re_process; + } + } + } + break; +#endif /* USE_TURN */ + + default: + assert(0); /* Can't happen */ + return; + } + if(!r) { + break; + } + + sc1=sc2; + } + if(!sc1){ + if (nr_ice_ctx_is_known_id(sock->ctx,((nr_stun_message_header*)buf)->id.octet)) + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is a retransmit",sock->ctx->label); + else + r_log(LOG_ICE,LOG_NOTICE,"ICE(%s): Message does not correspond to any registered stun ctx",sock->ctx->label); + } + } + else{ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Message is not STUN",sock->ctx->label); + + nr_ice_ctx_deliver_packet(sock->ctx, sock->component, &addr, buf, len); + } + + return; + } + +int nr_ice_socket_create(nr_ice_ctx *ctx,nr_ice_component *comp, nr_socket *nsock, int type, nr_ice_socket **sockp) + { + nr_ice_socket *sock=0; + NR_SOCKET fd; + nr_transport_addr addr; + int r,_status; + + if(!(sock=RCALLOC(sizeof(nr_ice_socket)))) + ABORT(R_NO_MEMORY); + + sock->sock=nsock; + sock->ctx=ctx; + sock->component=comp; + + if(r=nr_socket_getaddr(nsock, &addr)) + ABORT(r); + + if (type == NR_ICE_SOCKET_TYPE_DGRAM) { + assert(addr.protocol == IPPROTO_UDP); + } + else { + assert(addr.protocol == IPPROTO_TCP); + } + sock->type=type; + + TAILQ_INIT(&sock->candidates); + TAILQ_INIT(&sock->stun_ctxs); + + if (sock->type == NR_ICE_SOCKET_TYPE_DGRAM){ + if((r=nr_socket_getfd(nsock,&fd))) + ABORT(r); + NR_ASYNC_WAIT(fd,NR_ASYNC_WAIT_READ,nr_ice_socket_readable_cb,sock); + } + else if (sock->type == NR_ICE_SOCKET_TYPE_STREAM_TURN) { + /* some OS's (e.g. Linux) don't like to see un-connected TCP sockets in + * the poll socket set. */ + nr_socket_buffered_stun_set_readable_cb(nsock,nr_ice_socket_readable_cb,sock); + } + else if (sock->type == NR_ICE_SOCKET_TYPE_STREAM_TCP) { + /* in this case we can't hook up using NR_ASYNC_WAIT, because nr_socket_multi_tcp + consists of multiple nr_sockets and file descriptors. */ + if((r=nr_socket_multi_tcp_set_readable_cb(nsock,nr_ice_socket_readable_cb,sock))) + ABORT(r); + } + + *sockp=sock; + + _status=0; + abort: + if(_status) RFREE(sock); + return(_status); + } + + +int nr_ice_socket_destroy(nr_ice_socket **isockp) + { + nr_ice_stun_ctx *s1,*s2; + nr_ice_socket *isock; + + if(!isockp || !*isockp) + return(0); + + isock=*isockp; + *isockp=0; + + /* Close the socket */ + nr_ice_socket_close(isock); + + /* The STUN server */ + nr_stun_server_ctx_destroy(&isock->stun_server); + + /* Now clean up the STUN ctxs */ + TAILQ_FOREACH_SAFE(s1, &isock->stun_ctxs, entry, s2){ + TAILQ_REMOVE(&isock->stun_ctxs, s1, entry); + RFREE(s1); + } + + RFREE(isock); + + return(0); + } + +int nr_ice_socket_close(nr_ice_socket *isock) + { +#ifdef NR_SOCKET_IS_VOID_PTR + NR_SOCKET fd=NULL; + NR_SOCKET no_socket = NULL; +#else + NR_SOCKET fd=-1; + NR_SOCKET no_socket = -1; +#endif + + if (!isock||!isock->sock) + return(0); + + if (isock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP){ + nr_socket_getfd(isock->sock,&fd); + assert(isock->sock!=0); + if(fd != no_socket){ + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_READ); + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_WRITE); + } + } + nr_socket_destroy(&isock->sock); + + return(0); + } + +int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle) + { + nr_ice_stun_ctx *sc=0; + int _status; + + if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) + ABORT(R_NO_MEMORY); + + sc->type=NR_ICE_STUN_CLIENT; + sc->u.client=srv; + + TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); + + *handle=sc; + + _status=0; + abort: + return(_status); + } + +int nr_ice_socket_register_stun_server(nr_ice_socket *sock, nr_stun_server_ctx *srv,void **handle) + { + nr_ice_stun_ctx *sc=0; + int _status; + + if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) + ABORT(R_NO_MEMORY); + + sc->type=NR_ICE_STUN_SERVER; + sc->u.server=srv; + + TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); + + *handle=sc; + + _status=0; + abort: + return(_status); + } + +int nr_ice_socket_register_turn_client(nr_ice_socket *sock, nr_turn_client_ctx *srv, + nr_socket *turn_socket, void **handle) + { + nr_ice_stun_ctx *sc=0; + int _status; + + if(!(sc=RCALLOC(sizeof(nr_ice_stun_ctx)))) + ABORT(R_NO_MEMORY); + + sc->type=NR_ICE_TURN_CLIENT; + sc->u.turn_client.turn_client=srv; + sc->u.turn_client.turn_sock=turn_socket; + + TAILQ_INSERT_TAIL(&sock->stun_ctxs,sc,entry); + + *handle=sc; + + _status=0; + abort: + return(_status); + } + +/* Just mark it deregistered. Don't delete it now because it's not safe + in the CB, which is where this is likely to be called */ +int nr_ice_socket_deregister(nr_ice_socket *sock, void *handle) + { + nr_ice_stun_ctx *sc=handle; + + if(!sc) + return(0); + + sc->type=NR_ICE_STUN_NONE; + + return(0); + } + diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_socket.h b/media/mtransport/third_party/nICEr/src/ice/ice_socket.h new file mode 100644 index 000000000..b0fba1e1d --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.h @@ -0,0 +1,97 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_socket_h +#define _ice_socket_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +typedef struct nr_ice_stun_ctx_ { + int type; +#define NR_ICE_STUN_NONE 0 /* Deregistered */ +#define NR_ICE_STUN_CLIENT 1 +#define NR_ICE_STUN_SERVER 2 +#define NR_ICE_TURN_CLIENT 3 + + union { + nr_stun_client_ctx *client; + nr_stun_server_ctx *server; + struct { + nr_turn_client_ctx *turn_client; + nr_socket *turn_sock; /* The nr_socket_turn wrapped around + turn_client */ + } turn_client; + } u; + + TAILQ_ENTRY(nr_ice_stun_ctx_) entry; +} nr_ice_stun_ctx; + + +typedef struct nr_ice_socket_ { + int type; +#define NR_ICE_SOCKET_TYPE_DGRAM 1 +#define NR_ICE_SOCKET_TYPE_STREAM_TURN 2 +#define NR_ICE_SOCKET_TYPE_STREAM_TCP 3 + + nr_socket *sock; + nr_ice_ctx *ctx; + + nr_ice_candidate_head candidates; + nr_ice_component *component; + + TAILQ_HEAD(nr_ice_stun_ctx_head_,nr_ice_stun_ctx_) stun_ctxs; + + nr_stun_server_ctx *stun_server; + void *stun_server_handle; + + STAILQ_ENTRY(nr_ice_socket_) entry; +} nr_ice_socket; + +typedef STAILQ_HEAD(nr_ice_socket_head_,nr_ice_socket_) nr_ice_socket_head; + +int nr_ice_socket_create(struct nr_ice_ctx_ *ctx, struct nr_ice_component_ *comp, nr_socket *nsock, int type, nr_ice_socket **sockp); +int nr_ice_socket_destroy(nr_ice_socket **isock); +int nr_ice_socket_close(nr_ice_socket *isock); +int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle); +int nr_ice_socket_register_stun_server(nr_ice_socket *sock, nr_stun_server_ctx *srv,void **handle); +int nr_ice_socket_register_turn_client(nr_ice_socket *sock, nr_turn_client_ctx *srv,nr_socket *turn_socket, void **handle); +int nr_ice_socket_deregister(nr_ice_socket *sock, void *handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/net/local_addr.c b/media/mtransport/third_party/nICEr/src/net/local_addr.c new file mode 100644 index 000000000..c251f2215 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/local_addr.c @@ -0,0 +1,61 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <nr_api.h> +#include <string.h> +#include "local_addr.h" + +int nr_local_addr_copy(nr_local_addr *to, nr_local_addr *from) + { + nr_transport_addr_copy(&(to->addr), &(from->addr)); + to->interface = from->interface; + return(0); + } + +int nr_local_addr_fmt_info_string(nr_local_addr *addr, char *buf, int len) + { + int addr_type = addr->interface.type; + const char *vpn = (addr_type & NR_INTERFACE_TYPE_VPN) ? "VPN on " : ""; + + const char *type = (addr_type & NR_INTERFACE_TYPE_WIRED) ? "wired" : + (addr_type & NR_INTERFACE_TYPE_WIFI) ? "wifi" : + (addr_type & NR_INTERFACE_TYPE_MOBILE) ? "mobile" : + "unknown"; + + snprintf(buf, len, "%s%s, estimated speed: %d kbps", + vpn, type, addr->interface.estimated_speed); + buf[len - 1] = '\0'; + return (0); + } diff --git a/media/mtransport/third_party/nICEr/src/net/local_addr.h b/media/mtransport/third_party/nICEr/src/net/local_addr.h new file mode 100644 index 000000000..90e538856 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/local_addr.h @@ -0,0 +1,59 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _local_addr_h +#define _local_addr_h + +#include "transport_addr.h" + +typedef struct nr_interface_ { + int type; +#define NR_INTERFACE_TYPE_UNKNOWN 0x0 +#define NR_INTERFACE_TYPE_WIRED 0x1 +#define NR_INTERFACE_TYPE_WIFI 0x2 +#define NR_INTERFACE_TYPE_MOBILE 0x4 +#define NR_INTERFACE_TYPE_VPN 0x8 + int estimated_speed; /* Speed in kbps */ +} nr_interface; + +typedef struct nr_local_addr_ { + nr_transport_addr addr; + nr_interface interface; +} nr_local_addr; + +int nr_local_addr_copy(nr_local_addr *to, nr_local_addr *from); +int nr_local_addr_fmt_info_string(nr_local_addr *addr, char *buf, int len); + +#endif diff --git a/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c new file mode 100644 index 000000000..75e5f9546 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.c @@ -0,0 +1,88 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "nr_api.h" +#include "nr_interface_prioritizer.h" +#include "transport_addr.h" + +int nr_interface_prioritizer_create_int(void *obj, + nr_interface_prioritizer_vtbl *vtbl,nr_interface_prioritizer **ifpp) + { + int _status; + nr_interface_prioritizer *ifp=0; + + if(!(ifp=RCALLOC(sizeof(nr_interface_prioritizer)))) + ABORT(R_NO_MEMORY); + + ifp->obj = obj; + ifp->vtbl = vtbl; + + *ifpp = ifp; + + _status=0; + abort: + return(_status); + } + +int nr_interface_prioritizer_destroy(nr_interface_prioritizer **ifpp) + { + nr_interface_prioritizer *ifp; + + if (!ifpp || !*ifpp) + return(0); + + ifp = *ifpp; + *ifpp = 0; + ifp->vtbl->destroy(&ifp->obj); + RFREE(ifp); + return(0); + } + +int nr_interface_prioritizer_add_interface(nr_interface_prioritizer *ifp, + nr_local_addr *addr) + { + return ifp->vtbl->add_interface(ifp->obj, addr); + } + +int nr_interface_prioritizer_get_priority(nr_interface_prioritizer *ifp, + const char *key, UCHAR *interface_preference) + { + return ifp->vtbl->get_priority(ifp->obj,key,interface_preference); + } + +int nr_interface_prioritizer_sort_preference(nr_interface_prioritizer *ifp) + { + return ifp->vtbl->sort_preference(ifp->obj); + } diff --git a/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h new file mode 100644 index 000000000..c8a36526c --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_interface_prioritizer.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _nr_interface_prioritizer +#define _nr_interface_prioritizer + +#include "transport_addr.h" +#include "local_addr.h" + +typedef struct nr_interface_prioritizer_vtbl_ { + int (*add_interface)(void *obj, nr_local_addr *iface); + int (*get_priority)(void *obj, const char *key, UCHAR *pref); + int (*sort_preference)(void *obj); + int (*destroy)(void **obj); +} nr_interface_prioritizer_vtbl; + +typedef struct nr_interface_prioritizer_ { + void *obj; + nr_interface_prioritizer_vtbl *vtbl; +} nr_interface_prioritizer; + +int nr_interface_prioritizer_create_int(void *obj, nr_interface_prioritizer_vtbl *vtbl, + nr_interface_prioritizer **prioritizer); + +int nr_interface_prioritizer_destroy(nr_interface_prioritizer **prioritizer); + +int nr_interface_prioritizer_add_interface(nr_interface_prioritizer *prioritizer, + nr_local_addr *addr); + +int nr_interface_prioritizer_get_priority(nr_interface_prioritizer *prioritizer, + const char *key, UCHAR *interface_preference); + +int nr_interface_prioritizer_sort_preference(nr_interface_prioritizer *prioritizer); +#endif diff --git a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c new file mode 100644 index 000000000..b26f2d278 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c @@ -0,0 +1,686 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <nr_api.h> + +#include <assert.h> + +#include "nr_proxy_tunnel.h" + +#define MAX_HTTP_CONNECT_ADDR_SIZE 256 +#define MAX_HTTP_CONNECT_BUFFER_SIZE 1024 +#define MAX_ALPN_LENGTH 64 +#ifndef CRLF +#define CRLF "\r\n" +#endif +#define END_HEADERS CRLF CRLF + +typedef enum { + PROXY_TUNNEL_NONE=0, + PROXY_TUNNEL_REQUESTED, + PROXY_TUNNEL_CONNECTED, + PROXY_TUNNEL_CLOSED, + PROXY_TUNNEL_FAILED +} nr_socket_proxy_tunnel_state; + +typedef struct nr_socket_proxy_tunnel_ { + nr_proxy_tunnel_config *config; + nr_socket *inner; + nr_transport_addr remote_addr; + nr_socket_proxy_tunnel_state state; + char buffer[MAX_HTTP_CONNECT_BUFFER_SIZE]; + size_t buffered_bytes; + void *resolver_handle; +} nr_socket_proxy_tunnel; + +typedef struct nr_socket_wrapper_factory_proxy_tunnel_ { + nr_proxy_tunnel_config *config; +} nr_socket_wrapper_factory_proxy_tunnel; + +static int nr_socket_proxy_tunnel_destroy(void **objpp); +static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd); +static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp); +static int nr_socket_proxy_tunnel_connect(void *sock, nr_transport_addr *addr); +static int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len, size_t *written); +static int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen, size_t *len); +static int nr_socket_proxy_tunnel_close(void *obj); + +int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp); + +int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj, + nr_socket *inner, + nr_socket **socketpp); + +int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp); + +static nr_socket_vtbl nr_socket_proxy_tunnel_vtbl={ + 1, + nr_socket_proxy_tunnel_destroy, + 0, + 0, + nr_socket_proxy_tunnel_getfd, + nr_socket_proxy_tunnel_getaddr, + nr_socket_proxy_tunnel_connect, + nr_socket_proxy_tunnel_write, + nr_socket_proxy_tunnel_read, + nr_socket_proxy_tunnel_close +}; + +static int send_http_connect(nr_socket_proxy_tunnel *sock) +{ + int r, _status; + int port; + int printed; + char addr[MAX_HTTP_CONNECT_ADDR_SIZE]; + char mesg[MAX_HTTP_CONNECT_ADDR_SIZE + MAX_ALPN_LENGTH + 128]; + size_t offset = 0; + size_t bytes_sent; + + r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect"); + + if ((r=nr_transport_addr_get_port(&sock->remote_addr, &port))) { + ABORT(r); + } + + if ((r=nr_transport_addr_get_addrstring(&sock->remote_addr, addr, sizeof(addr)))) { + ABORT(r); + } + + printed = snprintf(mesg + offset, sizeof(mesg) - offset, + "CONNECT %s:%d HTTP/1.0", addr, port); + offset += printed; + if (printed < 0 || (offset >= sizeof(mesg))) { + ABORT(R_FAILED); + } + + if (sock->config->alpn) { + printed = snprintf(mesg + offset, sizeof(mesg) - offset, + CRLF "ALPN: %s", sock->config->alpn); + offset += printed; + if (printed < 0 || (offset >= sizeof(mesg))) { + ABORT(R_FAILED); + } + } + if (offset + sizeof(END_HEADERS) >= sizeof(mesg)) { + ABORT(R_FAILED); + } + memcpy(mesg + offset, END_HEADERS, strlen(END_HEADERS)); + offset += strlen(END_HEADERS); + + if ((r=nr_socket_write(sock->inner, mesg, offset, &bytes_sent, 0))) { + ABORT(r); + } + + if (bytes_sent < offset) { + /* TODO(bug 1116583): buffering and wait for */ + r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect should be buffering %lu", (unsigned long)bytes_sent); + ABORT(R_IO_ERROR); + } + + sock->state = PROXY_TUNNEL_REQUESTED; + + _status = 0; +abort: + return(_status); +} + +static char *find_http_terminator(char *response, size_t len) +{ + char *term = response; + char *end = response + len; + int N = strlen(END_HEADERS); + + for (; term = memchr(term, '\r', end - term); ++term) { + if (end - term >= N && memcmp(term, END_HEADERS, N) == 0) { + return term; + } + } + + return NULL; +} + +static int parse_http_response(char *begin, char *end, unsigned int *status) +{ + size_t len = end - begin; + char response[MAX_HTTP_CONNECT_BUFFER_SIZE + 1]; + + // len should *never* be greater than nr_socket_proxy_tunnel::buffered_bytes. + // Which in turn should never be greater nr_socket_proxy_tunnel::buffer size. + assert(len <= MAX_HTTP_CONNECT_BUFFER_SIZE); + if (len > MAX_HTTP_CONNECT_BUFFER_SIZE) { + return R_BAD_DATA; + } + + memcpy(response, begin, len); + response[len] = '\0'; + + // http://www.rfc-editor.org/rfc/rfc7230.txt + // status-line = HTTP-version SP status-code SP reason-phrase CRLF + // HTTP-version = HTTP-name "/" DIGIT "." DIGIT + // HTTP-name = "HTTP" ; "HTTP", case-sensitive + // status-code = 3DIGIT + + if (sscanf(response, "HTTP/%*u.%*u %u", status) != 1) { + r_log(LOG_GENERIC,LOG_WARNING,"parse_http_response failed to find status (%s)", response); + return R_BAD_DATA; + } + + return 0; +} + +static int nr_socket_proxy_tunnel_destroy(void **objpp) +{ + nr_socket_proxy_tunnel *sock; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_destroy"); + + if (!objpp || !*objpp) + return 0; + + sock = (nr_socket_proxy_tunnel *)*objpp; + *objpp = 0; + + if (sock->resolver_handle) { + nr_resolver_cancel(sock->config->resolver, sock->resolver_handle); + } + + nr_proxy_tunnel_config_destroy(&sock->config); + nr_socket_destroy(&sock->inner); + RFREE(sock); + + return 0; +} + +static int nr_socket_proxy_tunnel_getfd(void *obj, NR_SOCKET *fd) +{ + nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getfd"); + + return nr_socket_getfd(sock->inner, fd); +} + +static int nr_socket_proxy_tunnel_getaddr(void *obj, nr_transport_addr *addrp) +{ + nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel *)obj; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_getaddr"); + + return nr_socket_getaddr(sock->inner, addrp); +} + +static int nr_socket_proxy_tunnel_resolved_cb(void *obj, nr_transport_addr *proxy_addr) +{ + int r, _status; + nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_resolved_cb"); + + /* Mark the socket resolver as completed */ + sock->resolver_handle = 0; + + if (proxy_addr) { + r_log(LOG_GENERIC,LOG_DEBUG,"Resolved proxy address %s -> %s", + sock->config->proxy_host, proxy_addr->as_string); + } + else { + r_log(LOG_GENERIC,LOG_WARNING,"Failed to resolve proxy %s", + sock->config->proxy_host); + /* TODO: Mozilla bug 1241758: because of the callback the return value goes + * nowhere, so we can't mark the candidate as failed, so everything depends + * on the overall timeouts in this case. */ + sock->state = PROXY_TUNNEL_FAILED; + ABORT(R_NOT_FOUND); + } + + if ((r=nr_socket_connect(sock->inner, proxy_addr))) { + ABORT(r); + } + + _status = 0; +abort: + return(_status); +} + +int nr_socket_proxy_tunnel_connect(void *obj, nr_transport_addr *addr) +{ + int r, _status; + int has_addr; + nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj; + nr_proxy_tunnel_config *config = sock->config; + nr_transport_addr proxy_addr, local_addr; + nr_resolver_resource resource; + + if ((r=nr_transport_addr_copy(&sock->remote_addr, addr))) { + ABORT(r); + } + + assert(config->proxy_host); + + /* Check if the proxy_host is already an IP address */ + has_addr = !nr_str_port_to_transport_addr(config->proxy_host, + config->proxy_port, IPPROTO_TCP, &proxy_addr); + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: %s", config->proxy_host); + + if (!has_addr && !config->resolver) { + r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect name resolver not configured"); + ABORT(R_NOT_FOUND); + } + + if (!has_addr) { + resource.domain_name=config->proxy_host; + resource.port=config->proxy_port; + resource.stun_turn=NR_RESOLVE_PROTOCOL_TURN; + resource.transport_protocol=IPPROTO_TCP; + + if ((r=nr_socket_getaddr(sock->inner, &local_addr))) { + r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_connect failed to get local address"); + ABORT(r); + } + + switch(local_addr.ip_version) { + case NR_IPV4: + resource.address_family=AF_INET; + break; + case NR_IPV6: + resource.address_family=AF_INET6; + break; + default: + ABORT(R_BAD_ARGS); + } + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_connect: nr_resolver_resolve"); + if ((r=nr_resolver_resolve(config->resolver, &resource, + nr_socket_proxy_tunnel_resolved_cb, (void *)sock, &sock->resolver_handle))) { + r_log(LOG_GENERIC,LOG_ERR,"Could not invoke DNS resolver"); + ABORT(r); + } + + ABORT(R_WOULDBLOCK); + } + + if ((r=nr_socket_connect(sock->inner, &proxy_addr))) { + ABORT(r); + } + + _status=0; +abort: + return(_status); +} + +int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len, + size_t *written) +{ + int r, _status; + nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_write"); + + if (sock->state >= PROXY_TUNNEL_CLOSED) { + return R_FAILED; + } + + if (sock->state == PROXY_TUNNEL_NONE) { + if ((r=send_http_connect(sock))) { + ABORT(r); + } + } + + if (sock->state != PROXY_TUNNEL_CONNECTED) { + return R_WOULDBLOCK; + } + + if ((r=nr_socket_write(sock->inner, msg, len, written, 0))) { + ABORT(r); + } + + _status=0; +abort: + return(_status); +} + +int nr_socket_proxy_tunnel_read(void *obj, void * restrict buf, size_t maxlen, + size_t *len) +{ + int r, _status; + char *ptr, *http_term; + size_t bytes_read, available_buffer_len, maxlen_int; + size_t pending; + nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj; + unsigned int http_status; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_read"); + + *len = 0; + + if (sock->state >= PROXY_TUNNEL_CLOSED) { + return R_FAILED; + } + + if (sock->state == PROXY_TUNNEL_CONNECTED) { + return nr_socket_read(sock->inner, buf, maxlen, len, 0); + } + + if (sock->buffered_bytes >= sizeof(sock->buffer)) { + r_log(LOG_GENERIC,LOG_ERR,"buffer filled waiting for CONNECT response"); + assert(sock->buffered_bytes == sizeof(sock->buffer)); + ABORT(R_INTERNAL); + } + + /* Do not read more than maxlen bytes */ + available_buffer_len = sizeof(sock->buffer) - sock->buffered_bytes; + maxlen_int = maxlen < available_buffer_len ? maxlen : available_buffer_len; + if ((r=nr_socket_read(sock->inner, sock->buffer + sock->buffered_bytes, + maxlen_int, &bytes_read, 0))) { + ABORT(r); + } + + sock->buffered_bytes += bytes_read; + + if (http_term = find_http_terminator(sock->buffer, sock->buffered_bytes)) { + if ((r = parse_http_response(sock->buffer, http_term, &http_status))) { + ABORT(r); + } + + /* TODO (bug 1115934): Handle authentication challenges. */ + if (http_status < 200 || http_status >= 300) { + r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_read unable to connect %u", + http_status); + ABORT(R_FAILED); + } + + sock->state = PROXY_TUNNEL_CONNECTED; + + ptr = http_term + strlen(END_HEADERS); + pending = sock->buffered_bytes - (ptr - sock->buffer); + + if (pending == 0) { + ABORT(R_WOULDBLOCK); + } + + assert(pending <= maxlen); + *len = pending; + + memcpy(buf, ptr, *len); + } + + _status=0; +abort: + if (_status && _status != R_WOULDBLOCK) { + sock->state = PROXY_TUNNEL_FAILED; + } + return(_status); +} + +int nr_socket_proxy_tunnel_close(void *obj) +{ + nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_close"); + + if (sock->resolver_handle) { + nr_resolver_cancel(sock->config->resolver, sock->resolver_handle); + sock->resolver_handle = 0; + } + + sock->state = PROXY_TUNNEL_CLOSED; + + return nr_socket_close(sock->inner); +} + +int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **configpp) +{ + int _status; + nr_proxy_tunnel_config *configp=0; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_create"); + + if (!(configp=RCALLOC(sizeof(nr_proxy_tunnel_config)))) + ABORT(R_NO_MEMORY); + + *configpp=configp; + _status=0; +abort: + return(_status); +} + +int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **configpp) +{ + nr_proxy_tunnel_config *configp; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_destroy"); + + if (!configpp || !*configpp) + return 0; + + configp = *configpp; + *configpp = 0; + + RFREE(configp->proxy_host); + RFREE(configp->alpn); + RFREE(configp); + + return 0; +} + +int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config, + const char *host, UINT2 port) +{ + char *hostdup; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_proxy %s %d", host, port); + + if (!host) { + return R_BAD_ARGS; + } + + if (!(hostdup = r_strdup(host))) { + return R_NO_MEMORY; + } + + if (config->proxy_host) { + RFREE(config->proxy_host); + } + + config->proxy_host = hostdup; + config->proxy_port = port; + + return 0; +} + +int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config, + nr_resolver *resolver) +{ + r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_resolver"); + + config->resolver = resolver; + + return 0; +} + +int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config, + const char *alpn) +{ + r_log(LOG_GENERIC,LOG_DEBUG,"nr_proxy_tunnel_config_set_alpn"); + + if (alpn && (strlen(alpn) > MAX_ALPN_LENGTH)) { + return R_BAD_ARGS; + } + + if (config->alpn) { + RFREE(config->alpn); + } + + config->alpn = NULL; + + if (alpn) { + char *alpndup = r_strdup(alpn); + + if (!alpndup) { + return R_NO_MEMORY; + } + + config->alpn = alpndup; + } + + return 0; +} + +int nr_proxy_tunnel_config_copy(nr_proxy_tunnel_config *config, nr_proxy_tunnel_config **copypp) +{ + int r,_status; + nr_proxy_tunnel_config *copy = 0; + + if ((r=nr_proxy_tunnel_config_create(©))) + ABORT(r); + + if ((r=nr_proxy_tunnel_config_set_proxy(copy, config->proxy_host, config->proxy_port))) + ABORT(r); + + if ((r=nr_proxy_tunnel_config_set_resolver(copy, config->resolver))) + ABORT(r); + + if ((r=nr_proxy_tunnel_config_set_alpn(copy, config->alpn))) + ABORT(r); + + *copypp = copy; + + _status=0; +abort: + if (_status) { + nr_proxy_tunnel_config_destroy(©); + } + return(_status); +} + + +int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config, + nr_socket *inner, + nr_socket **socketpp) +{ + int r, _status; + nr_socket_proxy_tunnel *sock=0; + void *sockv; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_create"); + + if (!config) { + ABORT(R_BAD_ARGS); + } + + if (!(sock=RCALLOC(sizeof(nr_socket_proxy_tunnel)))) { + ABORT(R_NO_MEMORY); + } + + sock->inner = inner; + + if ((r=nr_proxy_tunnel_config_copy(config, &sock->config))) + ABORT(r); + + if ((r=nr_socket_create_int(sock, &nr_socket_proxy_tunnel_vtbl, socketpp))) { + ABORT(r); + } + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_created"); + + _status=0; +abort: + if (_status) { + sockv = sock; + nr_socket_proxy_tunnel_destroy(&sockv); + } + return(_status); +} + +int nr_socket_wrapper_factory_proxy_tunnel_wrap(void *obj, + nr_socket *inner, + nr_socket **socketpp) +{ + nr_socket_wrapper_factory_proxy_tunnel *wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)obj; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_wrap"); + + return nr_socket_proxy_tunnel_create(wrapper->config, inner, socketpp); +} + + +int nr_socket_wrapper_factory_proxy_tunnel_destroy(void **objpp) { + nr_socket_wrapper_factory_proxy_tunnel *wrapper; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_destroy"); + + if (!objpp || !*objpp) + return 0; + + wrapper = (nr_socket_wrapper_factory_proxy_tunnel *)*objpp; + *objpp = 0; + + nr_proxy_tunnel_config_destroy(&wrapper->config); + RFREE(wrapper); + + return 0; +} + +static nr_socket_wrapper_factory_vtbl proxy_tunnel_wrapper_vtbl = { + nr_socket_wrapper_factory_proxy_tunnel_wrap, + nr_socket_wrapper_factory_proxy_tunnel_destroy +}; + +int nr_socket_wrapper_factory_proxy_tunnel_create(nr_proxy_tunnel_config *config, + nr_socket_wrapper_factory **factory) { + int r,_status; + nr_socket_wrapper_factory_proxy_tunnel *wrapper=0; + void *wrapperv; + + r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_wrapper_factory_proxy_tunnel_create"); + + if (!(wrapper=RCALLOC(sizeof(nr_socket_wrapper_factory_proxy_tunnel)))) + ABORT(R_NO_MEMORY); + + if ((r=nr_proxy_tunnel_config_copy(config, &wrapper->config))) + ABORT(r); + + if ((r=nr_socket_wrapper_factory_create_int(wrapper, &proxy_tunnel_wrapper_vtbl, factory))) + ABORT(r); + + _status=0; +abort: + if (_status) { + wrapperv = wrapper; + nr_socket_wrapper_factory_proxy_tunnel_destroy(&wrapperv); + } + return(_status); +} diff --git a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h new file mode 100644 index 000000000..e9c4393e1 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.h @@ -0,0 +1,70 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _nr_proxy_tunnel_h +#define _nr_proxy_tunnel_h + +#include "nr_socket.h" +#include "nr_resolver.h" +#include "nr_socket_wrapper.h" + +typedef struct nr_proxy_tunnel_config_ { + nr_resolver *resolver; + char *proxy_host; + UINT2 proxy_port; + char *alpn; +} nr_proxy_tunnel_config; + +int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **config); + +int nr_proxy_tunnel_config_destroy(nr_proxy_tunnel_config **config); + +int nr_proxy_tunnel_config_set_proxy(nr_proxy_tunnel_config *config, + const char* host, UINT2 port); + +int nr_proxy_tunnel_config_set_resolver(nr_proxy_tunnel_config *config, + nr_resolver *resolver); + +int nr_proxy_tunnel_config_set_alpn(nr_proxy_tunnel_config *config, + const char *alpn); + +int nr_socket_proxy_tunnel_create(nr_proxy_tunnel_config *config, + nr_socket *inner, + nr_socket **socketpp); + +int nr_socket_wrapper_factory_proxy_tunnel_create(nr_proxy_tunnel_config *config, + nr_socket_wrapper_factory **factory); + +#endif diff --git a/media/mtransport/third_party/nICEr/src/net/nr_resolver.c b/media/mtransport/third_party/nICEr/src/net/nr_resolver.c new file mode 100644 index 000000000..4dbf1bbe9 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_resolver.c @@ -0,0 +1,85 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <nr_api.h> +#include "nr_resolver.h" + +int nr_resolver_create_int(void *obj, nr_resolver_vtbl *vtbl, nr_resolver **resolverp) +{ + int _status; + nr_resolver *resolver=0; + + if (!(resolver=RCALLOC(sizeof(nr_resolver)))) + ABORT(R_NO_MEMORY); + + resolver->obj=obj; + resolver->vtbl=vtbl; + + *resolverp=resolver; + _status=0; +abort: + return(_status); +} + +int nr_resolver_destroy(nr_resolver **resolverp) +{ + nr_resolver *resolver; + + if(!resolverp || !*resolverp) + return(0); + + resolver=*resolverp; + *resolverp=0; + + resolver->vtbl->destroy(&resolver->obj); + + RFREE(resolver); + + return(0); +} + +int nr_resolver_resolve(nr_resolver *resolver, + nr_resolver_resource *resource, + int (*cb)(void *cb_arg, nr_transport_addr *addr), + void *cb_arg, + void **handle) +{ + return resolver->vtbl->resolve(resolver->obj, resource, cb, cb_arg, handle); +} + +int nr_resolver_cancel(nr_resolver *resolver, void *handle) +{ + return resolver->vtbl->cancel(resolver->obj, handle); +} diff --git a/media/mtransport/third_party/nICEr/src/net/nr_resolver.h b/media/mtransport/third_party/nICEr/src/net/nr_resolver.h new file mode 100644 index 000000000..376ba9998 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_resolver.h @@ -0,0 +1,96 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _nr_resolver_h +#define _nr_resolver_h + +#include "transport_addr.h" + +#define NR_RESOLVE_PROTOCOL_STUN 1 +#define NR_RESOLVE_PROTOCOL_TURN 2 + +typedef struct nr_resolver_resource_ { + const char *domain_name; + UINT2 port; + int stun_turn; + UCHAR transport_protocol; + UCHAR address_family; +} nr_resolver_resource; + +typedef struct nr_resolver_vtbl_ { + int (*destroy)(void **obj); + int (*resolve)(void *obj, + nr_resolver_resource *resource, + int (*cb)(void *cb_arg, nr_transport_addr *addr), + void *cb_arg, + void **handle); + int (*cancel)(void *obj, void *handle); +} nr_resolver_vtbl; + +typedef struct nr_resolver_ { + void *obj; + nr_resolver_vtbl *vtbl; +} nr_resolver; + + +/* + The convention here is that the provider of this interface + must generate a void *obj, and a vtbl and then call + nr_resolver_create_int() to allocate the generic wrapper + object. + + The vtbl must contain implementations for all the functions + listed. + + The nr_resolver_destroy() function (and hence vtbl->destroy) + will be called when the consumer of the resolver is done + with it. That is the signal that it is safe to clean up + the resources associated with obj. No other function will + be called afterwards. +*/ +int nr_resolver_create_int(void *obj, nr_resolver_vtbl *vtbl, + nr_resolver **resolverp); +int nr_resolver_destroy(nr_resolver **resolverp); + +/* Request resolution of a domain */ +int nr_resolver_resolve(nr_resolver *resolver, + nr_resolver_resource *resource, + int (*cb)(void *cb_arg, nr_transport_addr *addr), + void *cb_arg, + void **handle); + +/* Cancel a requested resolution. No callback will fire. */ +int nr_resolver_cancel(nr_resolver *resolver, void *handle); +#endif diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket.c b/media/mtransport/third_party/nICEr/src/net/nr_socket.c new file mode 100644 index 000000000..d12f26454 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.c @@ -0,0 +1,191 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: nr_socket.c,v 1.2 2008/04/28 17:59:02 ekr Exp $"; + +#include <assert.h> +#include <nr_api.h> +#include "nr_socket.h" +#include "local_addr.h" + +#define CHECK_DEFINED(f) assert(sock->vtbl->f); if (!sock->vtbl->f) ERETURN(R_INTERNAL); +int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp) + { + int _status; + nr_socket *sock=0; + + if(!(sock=RCALLOC(sizeof(nr_socket)))) + ABORT(R_NO_MEMORY); + + assert(vtbl->version >= 1 && vtbl->version <= 2); + if (vtbl->version < 1 || vtbl->version > 2) + ABORT(R_INTERNAL); + + sock->obj=obj; + sock->vtbl=vtbl; + + *sockp=sock; + + _status=0; + abort: + return(_status); + } + +int nr_socket_destroy(nr_socket **sockp) + { + nr_socket *sock; + + if(!sockp || !*sockp) + return(0); + + + sock=*sockp; + *sockp=0; + + CHECK_DEFINED(destroy); + + assert(sock->vtbl); + if (sock->vtbl) + sock->vtbl->destroy(&sock->obj); + + RFREE(sock); + + return(0); + } + +int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len, int flags, + nr_transport_addr *addr) + { + CHECK_DEFINED(ssendto); + return sock->vtbl->ssendto(sock->obj,msg,len,flags,addr); + } + +int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen, + size_t *len, int flags, nr_transport_addr *addr) + { + CHECK_DEFINED(srecvfrom); + return sock->vtbl->srecvfrom(sock->obj, buf, maxlen, len, flags, addr); + } + +int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd) + { + CHECK_DEFINED(getfd); + return sock->vtbl->getfd(sock->obj, fd); + } + +int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp) + { + CHECK_DEFINED(getaddr); + return sock->vtbl->getaddr(sock->obj, addrp); + } + +int nr_socket_close(nr_socket *sock) + { + CHECK_DEFINED(close); + return sock->vtbl->close(sock->obj); + } + +int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr) + { + CHECK_DEFINED(connect); + return sock->vtbl->connect(sock->obj, addr); + } + +int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags) + { + CHECK_DEFINED(swrite); + return sock->vtbl->swrite(sock->obj, msg, len, written); + } + + +int nr_socket_read(nr_socket *sock,void * restrict buf, size_t maxlen, + size_t *len, int flags) + { + CHECK_DEFINED(sread); + return sock->vtbl->sread(sock->obj, buf, maxlen, len); + } + +int nr_socket_listen(nr_socket *sock, int backlog) + { + assert(sock->vtbl->version >=2 ); + CHECK_DEFINED(listen); + return sock->vtbl->listen(sock->obj, backlog); + } + +int nr_socket_accept(nr_socket *sock, nr_transport_addr *addrp, nr_socket **sockp) +{ + assert(sock->vtbl->version >= 2); + CHECK_DEFINED(accept); + return sock->vtbl->accept(sock->obj, addrp, sockp); +} + + +int nr_socket_factory_create_int(void *obj, + nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp) + { + int _status; + nr_socket_factory *factoryp=0; + + if(!(factoryp=RCALLOC(sizeof(nr_socket_factory)))) + ABORT(R_NO_MEMORY); + + factoryp->obj = obj; + factoryp->vtbl = vtbl; + + *factorypp = factoryp; + + _status=0; + abort: + return(_status); + } + +int nr_socket_factory_destroy(nr_socket_factory **factorypp) + { + nr_socket_factory *factoryp; + + if (!factorypp || !*factorypp) + return (0); + + factoryp = *factorypp; + *factorypp = NULL; + factoryp->vtbl->destroy(&factoryp->obj); + RFREE(factoryp); + return (0); + } + +int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp) + { + return factory->vtbl->create_socket(factory->obj, addr, sockp); + } + diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket.h b/media/mtransport/third_party/nICEr/src/net/nr_socket.h new file mode 100644 index 000000000..95f62e413 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket.h @@ -0,0 +1,121 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _nr_socket_h +#define _nr_socket_h + +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#endif + +#include "transport_addr.h" +#include "csi_platform.h" + +#ifdef __cplusplus +#define restrict +#elif defined(WIN32) +#define restrict __restrict +#endif + +typedef enum { + TCP_TYPE_NONE=0, + TCP_TYPE_ACTIVE, + TCP_TYPE_PASSIVE, + TCP_TYPE_SO, + TCP_TYPE_MAX +} nr_socket_tcp_type; + +typedef struct nr_socket_ nr_socket; + +typedef struct nr_socket_vtbl_ { + UINT4 version; /* Currently 2 */ + int (*destroy)(void **obj); + int (*ssendto)(void *obj,const void *msg, size_t len, int flags, + nr_transport_addr *addr); + int (*srecvfrom)(void *obj,void * restrict buf, size_t maxlen, size_t *len, int flags, + nr_transport_addr *addr); + int (*getfd)(void *obj, NR_SOCKET *fd); + int (*getaddr)(void *obj, nr_transport_addr *addrp); + int (*connect)(void *obj, nr_transport_addr *addr); + int (*swrite)(void *obj,const void *msg, size_t len, size_t *written); + int (*sread)(void *obj,void * restrict buf, size_t maxlen, size_t *len); + int (*close)(void *obj); + + /* available since version 2 */ + int (*listen)(void *obj, int backlog); + int (*accept)(void *obj, nr_transport_addr *addrp, nr_socket **sockp); +} nr_socket_vtbl; + + +struct nr_socket_ { + void *obj; + nr_socket_vtbl *vtbl; +}; + +typedef struct nr_socket_factory_vtbl_ { + int (*create_socket)(void *obj, nr_transport_addr *addr, nr_socket **sockp); + int (*destroy)(void **obj); +} nr_socket_factory_vtbl; + +typedef struct nr_socket_factory_ { + void *obj; + nr_socket_factory_vtbl *vtbl; +} nr_socket_factory; + +/* To be called by constructors */ +int nr_socket_create_int(void *obj, nr_socket_vtbl *vtbl, nr_socket **sockp); +int nr_socket_destroy(nr_socket **sockp); +int nr_socket_sendto(nr_socket *sock,const void *msg, size_t len, + int flags,nr_transport_addr *addr); +int nr_socket_recvfrom(nr_socket *sock,void * restrict buf, size_t maxlen, + size_t *len, int flags, nr_transport_addr *addr); +int nr_socket_getfd(nr_socket *sock, NR_SOCKET *fd); +int nr_socket_getaddr(nr_socket *sock, nr_transport_addr *addrp); +int nr_socket_close(nr_socket *sock); +int nr_socket_connect(nr_socket *sock, nr_transport_addr *addr); +int nr_socket_write(nr_socket *sock,const void *msg, size_t len, size_t *written, int flags); +int nr_socket_read(nr_socket *sock, void * restrict buf, size_t maxlen, size_t *len, int flags); +int nr_socket_listen(nr_socket *sock, int backlog); +int nr_socket_accept(nr_socket *sock, nr_transport_addr *addrp, nr_socket **sockp); + +int nr_socket_factory_create_int(void *obj, nr_socket_factory_vtbl *vtbl, nr_socket_factory **factorypp); +int nr_socket_factory_destroy(nr_socket_factory **factoryp); +int nr_socket_factory_create_socket(nr_socket_factory *factory, nr_transport_addr *addr, nr_socket **sockp); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_local.h b/media/mtransport/third_party/nICEr/src/net/nr_socket_local.h new file mode 100644 index 000000000..a2f813ff6 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_local.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _nr_socket_local_h +#define _nr_socket_local_h + +int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c new file mode 100644 index 000000000..12bbea795 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.c @@ -0,0 +1,620 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2014, Mozilla +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <assert.h> +#include <sys/types.h> + +#include "nr_api.h" +#include "ice_ctx.h" +#include "nr_socket.h" +#include "nr_socket_local.h" +#include "nr_socket_multi_tcp.h" +#include "nr_socket_buffered_stun.h" +#include "async_timer.h" + +typedef struct nr_tcp_socket_ctx_ { + nr_socket * inner; + nr_transport_addr remote_addr; + int is_framed; + + TAILQ_ENTRY(nr_tcp_socket_ctx_) entry; +} nr_tcp_socket_ctx; + +typedef TAILQ_HEAD(nr_tcp_socket_head_,nr_tcp_socket_ctx_) nr_tcp_socket_head; + +static void nr_tcp_socket_readable_cb(NR_SOCKET s, int how, void *arg); + +static int nr_tcp_socket_ctx_destroy(nr_tcp_socket_ctx **objp) + { + nr_tcp_socket_ctx *sock; + + if (!objp || !*objp) + return(0); + + sock=*objp; + *objp=0; + + nr_socket_destroy(&sock->inner); + + RFREE(sock); + + return(0); + } + +/* This takes ownership of nrsock whether it fails or not. */ +static int nr_tcp_socket_ctx_create(nr_socket *nrsock, int is_framed, + int max_pending, nr_tcp_socket_ctx **sockp) + { + int r, _status; + nr_tcp_socket_ctx *sock = 0; + nr_socket *tcpsock; + + if (!(sock = RCALLOC(sizeof(nr_tcp_socket_ctx)))) { + nr_socket_destroy(&nrsock); + ABORT(R_NO_MEMORY); + } + + if ((r=nr_socket_buffered_stun_create(nrsock, max_pending, is_framed ? ICE_TCP_FRAMING : TURN_TCP_FRAMING, &tcpsock))){ + nr_socket_destroy(&nrsock); + ABORT(r); + } + + sock->inner=tcpsock; + sock->is_framed=is_framed; + + if ((r=nr_ip4_port_to_transport_addr(ntohl(INADDR_ANY), 0, IPPROTO_TCP, &sock->remote_addr))) + ABORT(r); + + *sockp=sock; + + _status=0; +abort: + if (_status) { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d",__FILE__,__LINE__,__FUNCTION__,_status); + nr_tcp_socket_ctx_destroy(&sock); + } + return(_status); + } + +static int nr_tcp_socket_ctx_initialize(nr_tcp_socket_ctx *tcpsock, + nr_transport_addr *addr, void* cb_arg) + { + int r, _status; + NR_SOCKET fd; + + if ((r=nr_transport_addr_copy(&tcpsock->remote_addr, addr))) + ABORT(r); + if ((r=nr_socket_getfd(tcpsock->inner, &fd))) + ABORT(r); + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, nr_tcp_socket_readable_cb, cb_arg); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + return(_status); + } + +typedef struct nr_socket_multi_tcp_ { + nr_ice_ctx *ctx; + nr_socket *listen_socket; + nr_tcp_socket_head sockets; + nr_socket_tcp_type tcp_type; + nr_transport_addr addr; + NR_async_cb readable_cb; + void *readable_cb_arg; + int max_pending; +} nr_socket_multi_tcp; + +static int nr_socket_multi_tcp_destroy(void **objp); +static int nr_socket_multi_tcp_sendto(void *obj,const void *msg, size_t len, + int flags, nr_transport_addr *to); +static int nr_socket_multi_tcp_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from); +static int nr_socket_multi_tcp_getaddr(void *obj, nr_transport_addr *addrp); +static int nr_socket_multi_tcp_close(void *obj); +static int nr_socket_multi_tcp_connect(void *sock, nr_transport_addr *addr); +static int nr_socket_multi_tcp_listen(void *obj, int backlog); + +static nr_socket_vtbl nr_socket_multi_tcp_vtbl={ + 2, + nr_socket_multi_tcp_destroy, + nr_socket_multi_tcp_sendto, + nr_socket_multi_tcp_recvfrom, + 0, + nr_socket_multi_tcp_getaddr, + nr_socket_multi_tcp_connect, + 0, + 0, + nr_socket_multi_tcp_close, + nr_socket_multi_tcp_listen, + 0 +}; + +static int nr_socket_multi_tcp_create_stun_server_socket( + nr_socket_multi_tcp *sock, nr_ice_stun_server * stun_server, + nr_transport_addr *addr, int max_pending) + { + int r, _status; + nr_tcp_socket_ctx *tcp_socket_ctx=0; + nr_socket * nrsock; + + if (stun_server->transport!=IPPROTO_TCP) { + r_log(LOG_ICE,LOG_INFO,"%s:%d function %s skipping UDP STUN server(addr:%s)",__FILE__,__LINE__,__FUNCTION__,stun_server->u.addr.as_string); + ABORT(R_BAD_ARGS); + } + + if (stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR && + nr_transport_addr_cmp(&stun_server->u.addr,addr,NR_TRANSPORT_ADDR_CMP_MODE_VERSION)) { + r_log(LOG_ICE,LOG_INFO,"%s:%d function %s skipping STUN with different IP version (%u) than local socket (%u),",__FILE__,__LINE__,__FUNCTION__,stun_server->u.addr.ip_version,addr->ip_version); + ABORT(R_BAD_ARGS); + } + + if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory,addr, &nrsock))) + ABORT(r); + + /* This takes ownership of nrsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(nrsock, 0, max_pending, &tcp_socket_ctx))) + ABORT(r); + + if (stun_server->type == NR_ICE_STUN_SERVER_TYPE_ADDR) { + nr_transport_addr stun_server_addr; + + nr_transport_addr_copy(&stun_server_addr, &stun_server->u.addr); + r=nr_socket_connect(tcp_socket_ctx->inner, &stun_server_addr); + if (r && r!=R_WOULDBLOCK) { + r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s connect to STUN server(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,stun_server_addr.as_string,r); + ABORT(r); + } + + if ((r=nr_tcp_socket_ctx_initialize(tcp_socket_ctx, &stun_server_addr, sock))) + ABORT(r); + } + + TAILQ_INSERT_TAIL(&sock->sockets, tcp_socket_ctx, entry); + + _status=0; + abort: + if (_status) { + nr_tcp_socket_ctx_destroy(&tcp_socket_ctx); + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + } + return(_status); + } + +int nr_socket_multi_tcp_create(struct nr_ice_ctx_ *ctx, + nr_transport_addr *addr, nr_socket_tcp_type tcp_type, + int precreated_so_count, int max_pending, nr_socket **sockp) + { + int i=0; + int r, _status; + nr_socket_multi_tcp *sock=0; + nr_tcp_socket_ctx *tcp_socket_ctx; + nr_socket * nrsock; + + if (!(sock = RCALLOC(sizeof(nr_socket_multi_tcp)))) + ABORT(R_NO_MEMORY); + + TAILQ_INIT(&sock->sockets); + + sock->ctx=ctx; + sock->max_pending=max_pending; + sock->tcp_type=tcp_type; + nr_transport_addr_copy(&sock->addr, addr); + + if((tcp_type==TCP_TYPE_PASSIVE) && + ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, addr, &sock->listen_socket)))) + ABORT(r); + + if (tcp_type!=TCP_TYPE_ACTIVE) { + if (sock->ctx && sock->ctx->stun_servers) { + for (i=0; i<sock->ctx->stun_server_ct; ++i) { + if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock, + sock->ctx->stun_servers+i, addr, max_pending))) { + if (r!=R_BAD_ARGS) { + r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed to connect STUN server from addr:%s with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,r); + } + } + } + } + if (sock->ctx && sock->ctx->turn_servers) { + for (i=0; i<sock->ctx->turn_server_ct; ++i) { + if ((r=nr_socket_multi_tcp_create_stun_server_socket(sock, + &(sock->ctx->turn_servers[i]).turn_server, addr, max_pending))) { + if (r!=R_BAD_ARGS) { + r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed to connect TURN server from addr:%s with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,r); + } + } + } + } + } + + if ((tcp_type==TCP_TYPE_SO)) { + for (i=0; i<precreated_so_count; ++i) { + + if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, addr, &nrsock))) + ABORT(r); + + /* This takes ownership of nrsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(nrsock, 1, max_pending, &tcp_socket_ctx))){ + ABORT(r); + } + TAILQ_INSERT_TAIL(&sock->sockets, tcp_socket_ctx, entry); + } + } + + if((r=nr_socket_create_int(sock, &nr_socket_multi_tcp_vtbl, sockp))) + ABORT(r); + + _status=0; + abort: + if (_status) { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + nr_socket_multi_tcp_destroy((void**)&sock); + } + return(_status); + } + +int nr_socket_multi_tcp_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg) + { + nr_socket_multi_tcp *mtcp_sock = (nr_socket_multi_tcp *)sock->obj; + + mtcp_sock->readable_cb=readable_cb; + mtcp_sock->readable_cb_arg=readable_cb_arg; + + return 0; + } + +#define PREALLOC_CONNECT_FRAMED 0 +#define PREALLOC_CONNECT_NON_FRAMED 1 +#define PREALLOC_DONT_CONNECT_UNLESS_SO 2 + +static int nr_socket_multi_tcp_get_sock_connected_to(nr_socket_multi_tcp *sock, + nr_transport_addr *to, int preallocated_connect_mode, nr_socket **ret_sock) + { + int r, _status; + nr_tcp_socket_ctx *tcp_sock_ctx; + nr_socket * nrsock; + + to->protocol=IPPROTO_TCP; + + TAILQ_FOREACH(tcp_sock_ctx, &sock->sockets, entry) { + if (!nr_transport_addr_is_wildcard(&tcp_sock_ctx->remote_addr)) { + if (!nr_transport_addr_cmp(to, &tcp_sock_ctx->remote_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + *ret_sock=tcp_sock_ctx->inner; + return(0); + } + } + } + + tcp_sock_ctx=NULL; + /* not connected yet */ + if (sock->tcp_type != TCP_TYPE_ACTIVE) { + if (preallocated_connect_mode == PREALLOC_DONT_CONNECT_UNLESS_SO && sock->tcp_type != TCP_TYPE_SO) + ABORT(R_FAILED); + + /* find free preallocated socket and connect */ + TAILQ_FOREACH(tcp_sock_ctx, &sock->sockets, entry) { + if (nr_transport_addr_is_wildcard(&tcp_sock_ctx->remote_addr)) { + if (preallocated_connect_mode == PREALLOC_CONNECT_NON_FRAMED && tcp_sock_ctx->is_framed) + continue; + if (preallocated_connect_mode != PREALLOC_CONNECT_NON_FRAMED && !tcp_sock_ctx->is_framed) + continue; + + if ((r=nr_socket_connect(tcp_sock_ctx->inner, to))){ + if (r!=R_WOULDBLOCK) + ABORT(r); + } + + if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, to, sock))) + ABORT(r); + + *ret_sock=tcp_sock_ctx->inner; + + return(0); + } + } + tcp_sock_ctx=NULL; + ABORT(R_FAILED); + } + + /* if active type - create new socket for each new remote addr */ + assert(sock->tcp_type == TCP_TYPE_ACTIVE); + + if ((r=nr_socket_factory_create_socket(sock->ctx->socket_factory, &sock->addr, &nrsock))) + ABORT(r); + + /* This takes ownership of nrsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(nrsock, 1, sock->max_pending, &tcp_sock_ctx))){ + ABORT(r); + } + + TAILQ_INSERT_TAIL(&sock->sockets, tcp_sock_ctx, entry); + + if ((r=nr_socket_connect(tcp_sock_ctx->inner, to))){ + if (r!=R_WOULDBLOCK) + ABORT(r); + } + + if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, to, sock))) + ABORT(r); + + *ret_sock=tcp_sock_ctx->inner; + tcp_sock_ctx=NULL; + + _status=0; + abort: + if (_status) { + if (tcp_sock_ctx) { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d, tcp_sock_ctx remote_addr: %s",__FILE__,__LINE__,__FUNCTION__,_status, tcp_sock_ctx->remote_addr.as_string); + TAILQ_REMOVE(&sock->sockets, tcp_sock_ctx, entry); + nr_tcp_socket_ctx_destroy(&tcp_sock_ctx); + } else { + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s failed with error %d, tcp_sock_ctx=NULL",__FILE__,__LINE__,__FUNCTION__,_status); + } + } + + return(_status); + } + +int nr_socket_multi_tcp_stun_server_connect(nr_socket *sock, + nr_transport_addr *addr) + { + int r, _status; + nr_socket_multi_tcp *mtcp_sock = (nr_socket_multi_tcp *)sock->obj; + nr_socket *nrsock; + + assert(mtcp_sock->tcp_type != TCP_TYPE_ACTIVE); + if (mtcp_sock->tcp_type == TCP_TYPE_ACTIVE) + ABORT(R_INTERNAL); + + if ((r=nr_socket_multi_tcp_get_sock_connected_to(mtcp_sock,addr,PREALLOC_CONNECT_NON_FRAMED,&nrsock))) + ABORT(r); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + return(_status); + } + +static int nr_socket_multi_tcp_destroy(void **objp) + { + nr_socket_multi_tcp *sock; + nr_tcp_socket_ctx *tcpsock; + NR_SOCKET fd; + + if (!objp || !*objp) + return 0; + + sock=(nr_socket_multi_tcp *)*objp; + *objp=0; + + /* Cancel waiting on the socket */ + if (sock->listen_socket && !nr_socket_getfd(sock->listen_socket, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + nr_socket_destroy(&sock->listen_socket); + + while (!TAILQ_EMPTY(&sock->sockets)) { + + tcpsock = TAILQ_FIRST(&sock->sockets); + TAILQ_REMOVE(&sock->sockets, tcpsock, entry); + + if (!nr_socket_getfd(tcpsock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + nr_tcp_socket_ctx_destroy(&tcpsock); + } + + RFREE(sock); + + return 0; + } + +static int nr_socket_multi_tcp_sendto(void *obj, const void *msg, size_t len, + int flags, nr_transport_addr *to) + { + int r, _status; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_socket *nrsock; + + if ((r=nr_socket_multi_tcp_get_sock_connected_to(sock, to, + PREALLOC_DONT_CONNECT_UNLESS_SO, &nrsock))) + ABORT(r); + + if((r=nr_socket_sendto(nrsock, msg, len, flags, to))) + ABORT(r); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(to:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,to->as_string,_status); + + return(_status); +} + +static int nr_socket_multi_tcp_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from) + { + int r, _status = 0; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_tcp_socket_ctx *tcpsock; + + if (TAILQ_EMPTY(&sock->sockets)) + ABORT(R_FAILED); + + TAILQ_FOREACH(tcpsock, &sock->sockets, entry) { + if (nr_transport_addr_is_wildcard(&tcpsock->remote_addr)) + continue; + r=nr_socket_recvfrom(tcpsock->inner, buf, maxlen, len, flags, from); + if (!r) + return 0; + + if (r!=R_WOULDBLOCK) { + NR_SOCKET fd; + r_log(LOG_ICE,LOG_DEBUG, + "%s:%d function %s(to:%s) failed with error %d",__FILE__, + __LINE__,__FUNCTION__,tcpsock->remote_addr.as_string,r); + if (!nr_socket_getfd(tcpsock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + } + + TAILQ_REMOVE(&sock->sockets, tcpsock, entry); + nr_tcp_socket_ctx_destroy(&tcpsock); + ABORT(r); + } + } + + /* this also gets returned if all tcpsocks have wildcard remote_addr */ + _status=R_WOULDBLOCK; + abort: + + return(_status); + } + +static int nr_socket_multi_tcp_getaddr(void *obj, nr_transport_addr *addrp) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + + return nr_transport_addr_copy(addrp,&sock->addr); + } + +static int nr_socket_multi_tcp_close(void *obj) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_tcp_socket_ctx *tcpsock; + + if(sock->listen_socket) + nr_socket_close(sock->listen_socket); + + TAILQ_FOREACH(tcpsock, &sock->sockets, entry) { + nr_socket_close(tcpsock->inner); //ignore errors + } + + return 0; + } + +static void nr_tcp_socket_readable_cb(NR_SOCKET s, int how, void *arg) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)arg; + + // rearm + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, nr_tcp_socket_readable_cb, arg); + + if (sock->readable_cb) + sock->readable_cb(s, how, sock->readable_cb_arg); + } + +static int nr_socket_multi_tcp_connect(void *obj, nr_transport_addr *addr) + { + int r, _status; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + nr_socket *nrsock; + + if ((r=nr_socket_multi_tcp_get_sock_connected_to(sock,addr,PREALLOC_CONNECT_FRAMED,&nrsock))) + ABORT(r); + + _status=0; +abort: + if (_status) + r_log(LOG_ICE,LOG_DEBUG,"%s:%d function %s(addr:%s) failed with error %d",__FILE__,__LINE__,__FUNCTION__,addr->as_string,_status); + + return(_status); + } + +static void nr_tcp_multi_lsocket_readable_cb(NR_SOCKET s, int how, void *arg) + { + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)arg; + nr_socket *newsock; + nr_transport_addr remote_addr; + nr_tcp_socket_ctx *tcp_sock_ctx; + int r, _status; + + // rearm + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, nr_tcp_multi_lsocket_readable_cb, arg); + + /* accept */ + if ((r=nr_socket_accept(sock->listen_socket, &remote_addr, &newsock))) + ABORT(r); + + /* This takes ownership of newsock whether it fails or not. */ + if ((r=nr_tcp_socket_ctx_create(newsock, 1, sock->max_pending, &tcp_sock_ctx))) + ABORT(r); + + nr_socket_buffered_set_connected_to(tcp_sock_ctx->inner, &remote_addr); + + if ((r=nr_tcp_socket_ctx_initialize(tcp_sock_ctx, &remote_addr, sock))) { + nr_tcp_socket_ctx_destroy(&tcp_sock_ctx); + ABORT(r); + } + + TAILQ_INSERT_HEAD(&sock->sockets, tcp_sock_ctx, entry); + + _status=0; +abort: + if (_status) { + r_log(LOG_ICE,LOG_WARNING,"%s:%d %s failed to accept new TCP connection: %d",__FILE__,__LINE__,__FUNCTION__,_status); + } else { + r_log(LOG_ICE,LOG_INFO,"%s:%d %s accepted new TCP connection from %s",__FILE__,__LINE__,__FUNCTION__,remote_addr.as_string); + } + } + +static int nr_socket_multi_tcp_listen(void *obj, int backlog) + { + int r, _status; + nr_socket_multi_tcp *sock=(nr_socket_multi_tcp *)obj; + NR_SOCKET fd; + + if(!sock->listen_socket) + ABORT(R_FAILED); + + if ((r=nr_socket_listen(sock->listen_socket, backlog))) + ABORT(r); + + if ((r=nr_socket_getfd(sock->listen_socket, &fd))) + ABORT(r); + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, nr_tcp_multi_lsocket_readable_cb, sock); + + _status=0; + abort: + if (_status) + r_log(LOG_ICE,LOG_WARNING,"%s:%d function %s failed with error %d",__FILE__,__LINE__,__FUNCTION__,_status); + + return(_status); + } diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h new file mode 100644 index 000000000..a99e1652d --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_multi_tcp.h @@ -0,0 +1,52 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2014, Mozilla +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _nr_socket_multi_tcp_h +#define _nr_socket_multi_tcp_h + +#include "nr_socket.h" + +/* Argument use_framing is 0 only in call from test code (STUN TCP server + listening socket). For other purposes it should be always set to true */ + +int nr_socket_multi_tcp_create(struct nr_ice_ctx_ *ctx, + nr_transport_addr *addr, nr_socket_tcp_type tcp_type, + int precreated_so_count, int max_pending, nr_socket **sockp); + +int nr_socket_multi_tcp_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb,void *readable_cb_arg); + +int nr_socket_multi_tcp_stun_server_connect(nr_socket *sock, + nr_transport_addr *addr); + +#endif diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c new file mode 100644 index 000000000..0c9ec5674 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.c @@ -0,0 +1,82 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <nr_api.h> +#include "nr_socket_wrapper.h" + +int nr_socket_wrapper_factory_create_int(void *obj, nr_socket_wrapper_factory_vtbl *vtbl, + nr_socket_wrapper_factory **wrapperp) +{ + int _status; + nr_socket_wrapper_factory *wrapper=0; + + if (!(wrapper=RCALLOC(sizeof(nr_socket_wrapper_factory)))) + ABORT(R_NO_MEMORY); + + wrapper->obj=obj; + wrapper->vtbl=vtbl; + + *wrapperp=wrapper; + _status=0; +abort: + return(_status); +} + +int nr_socket_wrapper_factory_wrap(nr_socket_wrapper_factory *wrapper, + nr_socket *inner, + nr_socket **socketp) +{ + return wrapper->vtbl->wrap(wrapper->obj, inner, socketp); +} + +int nr_socket_wrapper_factory_destroy(nr_socket_wrapper_factory **wrapperp) +{ + nr_socket_wrapper_factory *wrapper; + + if (!wrapperp || !*wrapperp) + return 0; + + wrapper = *wrapperp; + *wrapperp = 0; + + assert(wrapper->vtbl); + if (wrapper->vtbl) + wrapper->vtbl->destroy(&wrapper->obj); + + RFREE(wrapper); + + return 0; +} + diff --git a/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h new file mode 100644 index 000000000..717518e23 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/nr_socket_wrapper.h @@ -0,0 +1,63 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _nr_socket_wrapper_h +#define _nr_socket_wrapper_h + +#include "nr_socket.h" + +typedef struct nr_socket_wrapper_factory_vtbl_ { + int (*wrap)(void *obj, + nr_socket *socket, + nr_socket **socketp); + int (*destroy)(void **obj); +} nr_socket_wrapper_factory_vtbl; + +typedef struct nr_socket_wrapper_factory_ { + void *obj; + nr_socket_wrapper_factory_vtbl *vtbl; +} nr_socket_wrapper_factory; + + +int nr_socket_wrapper_factory_create_int(void *obj, nr_socket_wrapper_factory_vtbl *vtbl, + nr_socket_wrapper_factory **wrapperp); + + +int nr_socket_wrapper_factory_wrap(nr_socket_wrapper_factory *wrapper, nr_socket *inner, + nr_socket **socketp); + +int nr_socket_wrapper_factory_destroy(nr_socket_wrapper_factory **wrapperp); + +#endif diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr.c b/media/mtransport/third_party/nICEr/src/net/transport_addr.c new file mode 100644 index 000000000..b44ad5b2c --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.c @@ -0,0 +1,476 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: transport_addr.c,v 1.2 2008/04/28 17:59:03 ekr Exp $"; + + +#include <csi_platform.h> +#include <stdio.h> +#include <memory.h> +#include <sys/types.h> +#include <errno.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <assert.h> +#include "nr_api.h" +#include "util.h" +#include "transport_addr.h" + +int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr) + { + int _status; + /* Max length for normalized IPv6 address string representation is 39 */ + char buffer[40]; + const char *protocol; + + switch(addr->protocol){ + case IPPROTO_TCP: + protocol = "TCP"; + break; + case IPPROTO_UDP: + protocol = "UDP"; + break; + default: + ABORT(R_INTERNAL); + } + + switch(addr->ip_version){ + case NR_IPV4: + if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer))) + strcpy(buffer, "[error]"); + snprintf(addr->as_string,sizeof(addr->as_string),"IP4:%s:%d/%s",buffer,(int)ntohs(addr->u.addr4.sin_port),protocol); + break; + case NR_IPV6: + if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer))) + strcpy(buffer, "[error]"); + snprintf(addr->as_string,sizeof(addr->as_string),"IP6:[%s]:%d/%s",buffer,(int)ntohs(addr->u.addr6.sin6_port),protocol); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len) + { + int _status; + char buffer[40]; + + switch(addr->ip_version){ + case NR_IPV4: + if (!inet_ntop(AF_INET, &addr->u.addr4.sin_addr,buffer,sizeof(buffer))) { + strncpy(buffer, "[error]", len); + } + break; + case NR_IPV6: + if (!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,buffer,sizeof(buffer))) { + strncpy(buffer, "[error]", len); + } + break; + default: + ABORT(R_INTERNAL); + } + snprintf(buf,len,"%s:%s",addr->ifname,buffer); + + _status=0; + abort: + return(_status); + } + +int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr) + { + int r,_status; + + if(!keep) memset(addr,0,sizeof(nr_transport_addr)); + + switch(protocol){ + case IPPROTO_TCP: + case IPPROTO_UDP: + break; + default: + ABORT(R_BAD_ARGS); + } + + addr->protocol=protocol; + + if(saddr->sa_family==AF_INET){ + addr->ip_version=NR_IPV4; + + memcpy(&addr->u.addr4,saddr,sizeof(struct sockaddr_in)); + addr->addr=(struct sockaddr *)&addr->u.addr4; + addr->addr_len=sizeof(struct sockaddr_in); + } + else if(saddr->sa_family==AF_INET6){ + addr->ip_version=NR_IPV6; + + memcpy(&addr->u.addr6, saddr, sizeof(struct sockaddr_in6)); + addr->addr=(struct sockaddr *)&addr->u.addr6; + addr->addr_len=sizeof(struct sockaddr_in6); + } + else + ABORT(R_BAD_ARGS); + + if(r=nr_transport_addr_fmt_addr_string(addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + + +int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from) + { + memcpy(to,from,sizeof(nr_transport_addr)); + to->addr=(struct sockaddr *)((char *)to + ((char *)from->addr - (char *)from)); + + return(0); + } + +int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, nr_transport_addr *from) + { + int r,_status; + char save_ifname[MAXIFNAME]; + + strncpy(save_ifname, to->ifname, MAXIFNAME); + save_ifname[MAXIFNAME-1]=0; /* Ensure null termination */ + + if (r=nr_transport_addr_copy(to, from)) + ABORT(r); + + strncpy(to->ifname, save_ifname, MAXIFNAME); + + if (r=nr_transport_addr_fmt_addr_string(to)) + ABORT(r); + + _status=0; + abort: + return _status; + } + +/* Convenience fxn. Is this the right API?*/ +int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr) + { + int r,_status; + + memset(addr, 0, sizeof(nr_transport_addr)); + + addr->ip_version=NR_IPV4; + addr->protocol=protocol; +#ifdef HAVE_SIN_LEN + addr->u.addr4.sin_len=sizeof(struct sockaddr_in); +#endif + addr->u.addr4.sin_family=PF_INET; + addr->u.addr4.sin_port=htons(port); + addr->u.addr4.sin_addr.s_addr=htonl(ip4); + addr->addr=(struct sockaddr *)&addr->u.addr4; + addr->addr_len=sizeof(struct sockaddr_in); + + if(r=nr_transport_addr_fmt_addr_string(addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_str_port_to_transport_addr(const char *ip, UINT2 port, int protocol, nr_transport_addr *addr_out) + { + int r,_status; + struct in_addr addr; + struct in6_addr addr6; + + if (inet_pton(AF_INET, ip, &addr) == 1) { + if(r=nr_ip4_port_to_transport_addr(ntohl(addr.s_addr),port,protocol,addr_out)) + ABORT(r); + } else if (inet_pton(AF_INET6, ip, &addr6) == 1) { + if(r=nr_ip6_port_to_transport_addr(&addr6,port,protocol,addr_out)) + ABORT(r); + } else { + ABORT(R_BAD_DATA); + } + + _status=0; + abort: + return(_status); + } + +int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr) + { + int r,_status; + + memset(addr, 0, sizeof(nr_transport_addr)); + + addr->ip_version=NR_IPV6; + addr->protocol=protocol; + addr->u.addr6.sin6_family=PF_INET6; + addr->u.addr6.sin6_port=htons(port); + memcpy(addr->u.addr6.sin6_addr.s6_addr, addr6->s6_addr, sizeof(addr6->s6_addr)); + addr->addr=(struct sockaddr *)&addr->u.addr6; + addr->addr_len=sizeof(struct sockaddr_in6); + + if(r=nr_transport_addr_fmt_addr_string(addr)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen) + { + int _status; + const char *res; + + switch(addr->ip_version){ + case NR_IPV4: + res = inet_ntop(AF_INET, &addr->u.addr4.sin_addr,str,maxlen); + break; + case NR_IPV6: + res = inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,str,maxlen); + break; + default: + ABORT(R_INTERNAL); + } + + if(!res){ + if (errno == ENOSPC){ + ABORT(R_BAD_ARGS); + } + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_get_port(nr_transport_addr *addr, int *port) + { + int _status; + + switch(addr->ip_version){ + case NR_IPV4: + *port=ntohs(addr->u.addr4.sin_port); + break; + case NR_IPV6: + *port=ntohs(addr->u.addr6.sin6_port); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +int nr_transport_addr_set_port(nr_transport_addr *addr, int port) + { + int _status; + + switch(addr->ip_version){ + case NR_IPV4: + addr->u.addr4.sin_port=htons(port); + break; + case NR_IPV6: + addr->u.addr6.sin6_port=htons(port); + break; + default: + ABORT(R_INTERNAL); + } + + _status=0; + abort: + return(_status); + } + +/* memcmp() may not work if, for instance, the string or interface + haven't been made. Hmmm.. */ +int nr_transport_addr_cmp(nr_transport_addr *addr1,nr_transport_addr *addr2,int mode) + { + assert(mode); + + if(addr1->ip_version != addr2->ip_version) + return(1); + + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL) + return(0); + + if(addr1->protocol != addr2->protocol) + return(1); + + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ADDR) + return(0); + + assert(addr1->addr_len == addr2->addr_len); + switch(addr1->ip_version){ + case NR_IPV4: + if(addr1->u.addr4.sin_addr.s_addr != addr2->u.addr4.sin_addr.s_addr) + return(1); + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ALL) + return(0); + if(addr1->u.addr4.sin_port != addr2->u.addr4.sin_port) + return(1); + break; + case NR_IPV6: + if(memcmp(addr1->u.addr6.sin6_addr.s6_addr,addr2->u.addr6.sin6_addr.s6_addr,sizeof(struct in6_addr))) + return(1); + if(mode < NR_TRANSPORT_ADDR_CMP_MODE_ALL) + return(0); + if(addr1->u.addr6.sin6_port != addr2->u.addr6.sin6_port) + return(1); + break; + default: + abort(); + } + + return(0); + } + +int nr_transport_addr_is_loopback(nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + switch(addr->u.addr4.sin_family){ + case AF_INET: + if (((ntohl(addr->u.addr4.sin_addr.s_addr)>>24)&0xff)==0x7f) + return 1; + break; + default: + UNIMPLEMENTED; + break; + } + break; + + case NR_IPV6: + if(!memcmp(addr->u.addr6.sin6_addr.s6_addr,in6addr_loopback.s6_addr,sizeof(struct in6_addr))) + return(1); + break; + default: + UNIMPLEMENTED; + } + + return(0); + } + +int nr_transport_addr_is_link_local(nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + /* RFC3927: 169.254/16 */ + if ((ntohl(addr->u.addr4.sin_addr.s_addr) & 0xFFFF0000) == 0xA9FE0000) + return(1); + break; + case NR_IPV6: + { + UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr); + if ((*addrTop & htonl(0xFFC00000)) == htonl(0xFE800000)) + return(2); + } + break; + default: + UNIMPLEMENTED; + } + + return(0); + } + +int nr_transport_addr_is_wildcard(nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + if(addr->u.addr4.sin_addr.s_addr==INADDR_ANY) + return(1); + if(addr->u.addr4.sin_port==0) + return(1); + break; + case NR_IPV6: + if(!memcmp(addr->u.addr6.sin6_addr.s6_addr,in6addr_any.s6_addr,sizeof(struct in6_addr))) + return(1); + if(addr->u.addr6.sin6_port==0) + return(1); + break; + default: + UNIMPLEMENTED; + } + + return(0); + } + +nr_transport_addr_mask nr_private_ipv4_addrs[] = { + /* RFC1918: 10/8 */ + {0x0A000000, 0xFF000000}, + /* RFC1918: 172.16/12 */ + {0xAC100000, 0xFFF00000}, + /* RFC1918: 192.168/16 */ + {0xC0A80000, 0xFFFF0000}, + /* RFC6598: 100.64/10 */ + {0x64400000, 0xFFC00000} +}; + +int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr) + { + switch(addr->ip_version){ + case NR_IPV4: + { + UINT4 ip = ntohl(addr->u.addr4.sin_addr.s_addr); + for (int i=0; i<(sizeof(nr_private_ipv4_addrs)/sizeof(nr_transport_addr_mask)); i++) { + if ((ip & nr_private_ipv4_addrs[i].mask) == nr_private_ipv4_addrs[i].addr) + return i + 1; + } + } + break; + case NR_IPV6: + return(0); + default: + UNIMPLEMENTED; + } + + return(0); + } + +int nr_transport_addr_is_reliable_transport(nr_transport_addr *addr) + { + return addr->protocol == IPPROTO_TCP; + } diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr.h b/media/mtransport/third_party/nICEr/src/net/transport_addr.h new file mode 100644 index 000000000..dfec63329 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.h @@ -0,0 +1,103 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _transport_addr_h +#define _transport_addr_h + +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +/* Length of a string hex representation of a MD5 hash */ +#define MAXIFNAME 33 + +/* Generic transport address + + This spans both sockaddr_in and sockaddr_in6 + */ +typedef struct nr_transport_addr_ { + UCHAR ip_version; /* 4 or 6 */ +#define NR_IPV4 4 +#define NR_IPV6 6 + UCHAR protocol; /* IPPROTO_TCP, IPPROTO_UDP */ + struct sockaddr *addr; + int addr_len; + union { + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } u; + char ifname[MAXIFNAME]; + /* A string version. + 56 = 5 ("IP6:[") + 39 (ipv6 address) + 2 ("]:") + 5 (port) + 4 (/UDP) + 1 (null) */ + char as_string[56]; +} nr_transport_addr; + +typedef struct nr_transport_addr_mask_ { + UINT4 addr; + UINT4 mask; +} nr_transport_addr_mask; + +int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr); + +// addresses, ports in local byte order +int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr); +int nr_str_port_to_transport_addr(const char *str, UINT2 port, int protocol, nr_transport_addr *addr); +int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr); + +int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen); +int nr_transport_addr_get_port(nr_transport_addr *addr, int *port); +int nr_transport_addr_cmp(nr_transport_addr *addr1,nr_transport_addr *addr2,int mode); +#define NR_TRANSPORT_ADDR_CMP_MODE_VERSION 1 +#define NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL 2 +#define NR_TRANSPORT_ADDR_CMP_MODE_ADDR 3 +#define NR_TRANSPORT_ADDR_CMP_MODE_ALL 4 + +int nr_transport_addr_is_wildcard(nr_transport_addr *addr); +int nr_transport_addr_is_loopback(nr_transport_addr *addr); +int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr); +int nr_transport_addr_is_link_local(nr_transport_addr *addr); +int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from); +int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, nr_transport_addr *from); +int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr); +int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len); +int nr_transport_addr_set_port(nr_transport_addr *addr, int port); +int nr_transport_addr_is_reliable_transport(nr_transport_addr *addr); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.c b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.c new file mode 100644 index 000000000..6a8a64bb9 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.c @@ -0,0 +1,233 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: transport_addr_reg.c,v 1.2 2008/04/28 17:59:03 ekr Exp $"; + +#include <csi_platform.h> +#include <stdio.h> +#include <string.h> +#include <memory.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock2.h> +#else +#include <strings.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif +#include <assert.h> +#include "nr_api.h" +#include "util.h" +#include "transport_addr.h" +#include "transport_addr_reg.h" + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 /* Value used by linux/BSD */ +#endif + +int +nr_reg_get_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr) +{ + int r,_status; + unsigned int count; + char *address = 0; + UINT2 port = 0; + char *ifname = 0; + char *protocol = 0; + int p; + + if ((r=NR_reg_get_child_count(prefix, &count))) + ABORT(r); + + if (count == 0) + ABORT(R_NOT_FOUND); + + if ((r=NR_reg_alloc2_string(prefix, "address", &address))) { + if (r != R_NOT_FOUND) + ABORT(r); + address = 0; + } + + if ((r=NR_reg_alloc2_string(prefix, "ifname", &ifname))) { + if (r != R_NOT_FOUND) + ABORT(r); + ifname = 0; + } + + if ((r=NR_reg_get2_uint2(prefix, "port", &port))) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 0; + } + + if ((r=NR_reg_alloc2_string(prefix, "protocol", &protocol))) { + if (r != R_NOT_FOUND) + ABORT(r); + p = IPPROTO_UDP; + + protocol = 0; + } + else { + if (!strcasecmp("tcp", protocol)) + p = IPPROTO_TCP; + else if (!strcasecmp("udp", protocol)) + p = IPPROTO_UDP; + else + ABORT(R_BAD_DATA); + } + + if (!keep) memset(addr, 0, sizeof(*addr)); + + if ((r=nr_str_port_to_transport_addr(address?address:"0.0.0.0", port, p, addr))) + ABORT(r); + + if (ifname) + strlcpy(addr->ifname, ifname, sizeof(addr->ifname)); + + _status=0; + abort: + RFREE(protocol); + RFREE(ifname); + RFREE(address); + return(_status); +} + +int +nr_reg_set_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr) +{ + int r,_status; + + if (! keep) { + if ((r=NR_reg_del(prefix))) + ABORT(r); + } + + switch (addr->ip_version) { + case NR_IPV4: + if (!nr_transport_addr_is_wildcard(addr)) { + if ((r=NR_reg_set2_string(prefix, "address", inet_ntoa(addr->u.addr4.sin_addr)))) + ABORT(r); + } + + if (addr->u.addr4.sin_port != 0) { + if ((r=NR_reg_set2_uint2(prefix, "port", ntohs(addr->u.addr4.sin_port)))) + ABORT(r); + } + break; + + case NR_IPV6: + if (!nr_transport_addr_is_wildcard(addr)) { + char address[INET6_ADDRSTRLEN]; + if(!inet_ntop(AF_INET6, &addr->u.addr6.sin6_addr,address,sizeof(address))) { + ABORT(R_BAD_DATA); + } + + if ((r=NR_reg_set2_string(prefix, "address", address))) { + ABORT(r); + } + } + + if (addr->u.addr6.sin6_port != 0) { + if ((r=NR_reg_set2_uint2(prefix, "port", ntohs(addr->u.addr6.sin6_port)))) + ABORT(r); + } + break; + default: + ABORT(R_INTERNAL); + break; + } + + /* We abort if neither NR_IPV4 or NR_IPV6 above */ + switch (addr->protocol) { + case IPPROTO_TCP: + if ((r=NR_reg_set2_string(prefix, "protocol", "tcp"))) + ABORT(r); + break; + case IPPROTO_UDP: + if ((r=NR_reg_set2_string(prefix, "protocol", "udp"))) + ABORT(r); + break; + default: + UNIMPLEMENTED; + break; + } + + if (strlen(addr->ifname) > 0) { + if ((r=NR_reg_set2_string(prefix, "ifname", addr->ifname))) + ABORT(r); + } + + _status=0; + abort: + if (_status) + NR_reg_del(prefix); + return _status; +} + +int +nr_reg_get_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr) +{ + int r, _status; + NR_registry registry; + + if ((r=NR_reg_make_registry(prefix, name, registry))) + ABORT(r); + + if ((r=nr_reg_get_transport_addr(registry, keep, addr))) + ABORT(r); + + _status = 0; +abort: + return _status; +} + +int +nr_reg_set_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr) +{ + int r, _status; + NR_registry registry; + + if ((r=NR_reg_make_registry(prefix, name, registry))) + ABORT(r); + + if ((r=nr_reg_set_transport_addr(registry, keep, addr))) + ABORT(r); + + _status = 0; +abort: + return _status; +} + diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.h b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.h new file mode 100644 index 000000000..761953a9c --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/net/transport_addr_reg.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _transport_addr_reg_h +#define _transport_addr_reg_h + +#include "registry.h" + +int nr_reg_get_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr); +int nr_reg_set_transport_addr(NR_registry prefix, int keep, nr_transport_addr *addr); +int nr_reg_get_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr); +int nr_reg_set_transport_addr2(NR_registry prefix, char *name, int keep, nr_transport_addr *addr); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/addrs.c b/media/mtransport/third_party/nICEr/src/stun/addrs.c new file mode 100644 index 000000000..c90191f73 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/addrs.c @@ -0,0 +1,444 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: addrs.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <csi_platform.h> +#include <assert.h> +#include <string.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <iphlpapi.h> +#include <tchar.h> +#else /* !WIN32 */ + +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <errno.h> + +#ifndef ANDROID +/* This works on linux and BSD, but not android */ +#include <sys/types.h> /* getifaddrs */ +#include <ifaddrs.h> /* getifaddrs */ +#else +#include "ifaddrs-android.h" +#define getifaddrs android_getifaddrs +#define freeifaddrs android_freeifaddrs +#endif + +#ifdef LINUX + +#ifdef ANDROID +/* Work around an Android NDK < r8c bug */ +#undef __unused +#else +#include <linux/if.h> /* struct ifreq, IFF_POINTTOPOINT */ +#include <linux/wireless.h> /* struct iwreq */ +#include <linux/ethtool.h> /* struct ethtool_cmd */ +#include <linux/sockios.h> /* SIOCETHTOOL */ +#endif /* ANDROID */ + +#endif /* LINUX */ + +#endif /* !WIN32 */ + +#include "stun.h" +#include "addrs.h" +#include "nr_crypto.h" +#include "util.h" + +#if defined(WIN32) + +#define WIN32_MAX_NUM_INTERFACES 20 + +#define NR_MD5_HASH_LENGTH 16 + +#define _NR_MAX_KEY_LENGTH 256 +#define _NR_MAX_NAME_LENGTH 512 + +#define _ADAPTERS_BASE_REG "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +static int nr_win32_get_adapter_friendly_name(char *adapter_GUID, char **friendly_name) +{ + int r,_status; + HKEY adapter_reg; + TCHAR adapter_key[_NR_MAX_KEY_LENGTH]; + TCHAR keyval_buf[_NR_MAX_KEY_LENGTH]; + TCHAR adapter_GUID_tchar[_NR_MAX_NAME_LENGTH]; + DWORD keyval_len, key_type; + size_t converted_chars, newlen; + char *my_fn = 0; + +#ifdef _UNICODE + mbstowcs_s(&converted_chars, adapter_GUID_tchar, strlen(adapter_GUID)+1, + adapter_GUID, _TRUNCATE); +#else + strlcpy(adapter_GUID_tchar, adapter_GUID, _NR_MAX_NAME_LENGTH); +#endif + + _tcscpy_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT(_ADAPTERS_BASE_REG)); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\")); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, adapter_GUID_tchar); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\Connection")); + + r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, adapter_key, 0, KEY_READ, &adapter_reg); + + if (r != ERROR_SUCCESS) { + r_log(NR_LOG_STUN, LOG_ERR, "Got error %d opening adapter reg key\n", r); + ABORT(R_INTERNAL); + } + + keyval_len = sizeof(keyval_buf); + r = RegQueryValueEx(adapter_reg, TEXT("Name"), NULL, &key_type, + (BYTE *)keyval_buf, &keyval_len); + + RegCloseKey(adapter_reg); + +#ifdef UNICODE + newlen = wcslen(keyval_buf)+1; + my_fn = (char *) RCALLOC(newlen); + if (!my_fn) { + ABORT(R_NO_MEMORY); + } + wcstombs_s(&converted_chars, my_fn, newlen, keyval_buf, _TRUNCATE); +#else + my_fn = r_strdup(keyval_buf); +#endif + + *friendly_name = my_fn; + _status=0; + +abort: + if (_status) { + if (my_fn) free(my_fn); + } + return(_status); +} + +static int +stun_get_win32_addrs(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r, _status; + PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL, tmpAddress = NULL; + // recomended per https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx + static const ULONG initialBufLen = 15000; + ULONG buflen = initialBufLen; + char bin_hashed_ifname[NR_MD5_HASH_LENGTH]; + char hex_hashed_ifname[MAXIFNAME]; + int n = 0; + + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + /* According to MSDN (see above) we have try GetAdapterAddresses() multiple times */ + for (n = 0; n < 5; n++) { + AdapterAddresses = (PIP_ADAPTER_ADDRESSES) RMALLOC(buflen); + if (AdapterAddresses == NULL) { + r_log(NR_LOG_STUN, LOG_ERR, "Error allocating buf for GetAdaptersAddresses()"); + ABORT(R_NO_MEMORY); + } + + r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, AdapterAddresses, &buflen); + if (r == NO_ERROR) { + break; + } + r_log(NR_LOG_STUN, LOG_ERR, "GetAdaptersAddresses() returned error (%d)", r); + RFREE(AdapterAddresses); + AdapterAddresses = NULL; + } + + if (n >= 5) { + r_log(NR_LOG_STUN, LOG_ERR, "5 failures calling GetAdaptersAddresses()"); + ABORT(R_INTERNAL); + } + + n = 0; + + /* Loop through the adapters */ + + for (tmpAddress = AdapterAddresses; tmpAddress != NULL; tmpAddress = tmpAddress->Next) { + + if (tmpAddress->OperStatus != IfOperStatusUp) + continue; + + if ((tmpAddress->IfIndex != 0) || (tmpAddress->Ipv6IfIndex != 0)) { + IP_ADAPTER_UNICAST_ADDRESS *u = 0; + + if(r=nr_crypto_md5((UCHAR *)tmpAddress->FriendlyName, + wcslen(tmpAddress->FriendlyName) * sizeof(wchar_t), + bin_hashed_ifname)) + ABORT(r); + if(r=nr_bin2hex(bin_hashed_ifname, sizeof(bin_hashed_ifname), + hex_hashed_ifname)) + ABORT(r); + + for (u = tmpAddress->FirstUnicastAddress; u != 0; u = u->Next) { + SOCKET_ADDRESS *sa_addr = &u->Address; + + if ((sa_addr->lpSockaddr->sa_family == AF_INET) || + (sa_addr->lpSockaddr->sa_family == AF_INET6)) { + if ((r=nr_sockaddr_to_transport_addr((struct sockaddr*)sa_addr->lpSockaddr, IPPROTO_UDP, 0, &(addrs[n].addr)))) + ABORT(r); + } + else { + r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized sa_family for address on adapter %lu", tmpAddress->IfIndex); + continue; + } + + strlcpy(addrs[n].addr.ifname, hex_hashed_ifname, sizeof(addrs[n].addr.ifname)); + if (tmpAddress->IfType == IF_TYPE_ETHERNET_CSMACD) { + addrs[n].interface.type = NR_INTERFACE_TYPE_WIRED; + } else if (tmpAddress->IfType == IF_TYPE_IEEE80211) { + /* Note: this only works for >= Win Vista */ + addrs[n].interface.type = NR_INTERFACE_TYPE_WIFI; + } else { + addrs[n].interface.type = NR_INTERFACE_TYPE_UNKNOWN; + } +#if (_WIN32_WINNT >= 0x0600) + /* Note: only >= Vista provide link speed information */ + addrs[n].interface.estimated_speed = tmpAddress->TransmitLinkSpeed / 1000; +#else + addrs[n].interface.estimated_speed = 0; +#endif + if (++n >= maxaddrs) + goto done; + } + } + } + + done: + *count = n; + _status = 0; + + abort: + RFREE(AdapterAddresses); + return _status; +} + +#else /* WIN32 */ + +static int +nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr); + +static int +stun_getifaddrs(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r,_status; + struct ifaddrs* if_addrs_head=NULL; + struct ifaddrs* if_addr; + + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + if (getifaddrs(&if_addrs_head) == -1) { + r_log(NR_LOG_STUN, LOG_ERR, "getifaddrs error e = %d", errno); + ABORT(R_INTERNAL); + } + + if_addr = if_addrs_head; + + while (if_addr && *count < maxaddrs) { + /* This can be null */ + if (if_addr->ifa_addr) { + switch (if_addr->ifa_addr->sa_family) { + case AF_INET: + case AF_INET6: + if (r=nr_sockaddr_to_transport_addr(if_addr->ifa_addr, IPPROTO_UDP, 0, &(addrs[*count].addr))) { + r_log(NR_LOG_STUN, LOG_ERR, "nr_sockaddr_to_transport_addr error r = %d", r); + } else { +#if defined(LINUX) && !defined(ANDROID) + struct ethtool_cmd ecmd; + struct ifreq ifr; + struct iwreq wrq; + int e; + int s = socket(AF_INET, SOCK_DGRAM, 0); + + strncpy(ifr.ifr_name, if_addr->ifa_name, sizeof(ifr.ifr_name)); + /* TODO (Bug 896851): interface property for Android */ + /* Getting ethtool for ethernet information. */ + ecmd.cmd = ETHTOOL_GSET; + /* In/out param */ + ifr.ifr_data = (void*)&ecmd; + + e = ioctl(s, SIOCETHTOOL, &ifr); + if (e == 0) + { + /* For wireless network, we won't get ethtool, it's a wired + * connection */ + addrs[*count].interface.type = NR_INTERFACE_TYPE_WIRED; +#ifdef DONT_HAVE_ETHTOOL_SPEED_HI + addrs[*count].interface.estimated_speed = ecmd.speed; +#else + addrs[*count].interface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000; +#endif + } + + strncpy(wrq.ifr_name, if_addr->ifa_name, sizeof(wrq.ifr_name)); + e = ioctl(s, SIOCGIWRATE, &wrq); + if (e == 0) + { + addrs[*count].interface.type = NR_INTERFACE_TYPE_WIFI; + addrs[*count].interface.estimated_speed = wrq.u.bitrate.value / 1000; + } + + close(s); + + if (if_addr->ifa_flags & IFF_POINTOPOINT) + { + addrs[*count].interface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN; + /* TODO (Bug 896913): find backend network type of this VPN */ + } +#else + addrs[*count].interface.type = NR_INTERFACE_TYPE_UNKNOWN; + addrs[*count].interface.estimated_speed = 0; +#endif + strlcpy(addrs[*count].addr.ifname, if_addr->ifa_name, sizeof(addrs[*count].addr.ifname)); + ++(*count); + } + break; + default: + ; + } + } + + if_addr = if_addr->ifa_next; + } + + _status=0; +abort: + if (if_addrs_head) { + freeifaddrs(if_addrs_head); + } + return(_status); +} + +#endif + +static int +nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr) +{ + int i; + int different; + + for (i = 0; i < count; ++i) { + different = nr_transport_addr_cmp(&addrs[i].addr, &(addr->addr), + NR_TRANSPORT_ADDR_CMP_MODE_ALL); + if (!different) + return 1; /* duplicate */ + } + + return 0; +} + +int +nr_stun_remove_duplicate_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count) +{ + int r, _status; + nr_local_addr *tmp = 0; + int i; + int n; + + tmp = RMALLOC(*count * sizeof(*tmp)); + if (!tmp) + ABORT(R_NO_MEMORY); + + n = 0; + for (i = 0; i < *count; ++i) { + if (nr_stun_is_duplicate_addr(tmp, n, &addrs[i])) { + /* skip addrs[i], it's a duplicate */ + } + else if (remove_loopback && nr_transport_addr_is_loopback(&addrs[i].addr)) { + /* skip addrs[i], it's a loopback */ + } + else if (remove_link_local && + addrs[i].addr.ip_version == NR_IPV6 && + nr_transport_addr_is_link_local(&addrs[i].addr)) { + /* skip addrs[i], it's a link-local address */ + } + else { + /* otherwise, copy it to the temporary array */ + if ((r=nr_local_addr_copy(&tmp[n], &addrs[i]))) + ABORT(r); + ++n; + } + } + + *count = n; + + memset(addrs, 0, *count * sizeof(*addrs)); + /* copy temporary array into passed in/out array */ + for (i = 0; i < *count; ++i) { + if ((r=nr_local_addr_copy(&addrs[i], &tmp[i]))) + ABORT(r); + } + + _status = 0; + abort: + RFREE(tmp); + return _status; +} + +#ifndef USE_PLATFORM_NR_STUN_GET_ADDRS + +int +nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int drop_loopback, int drop_link_local, int *count) +{ + int r,_status=0; + int i; + char typestr[100]; + +#ifdef WIN32 + _status = stun_get_win32_addrs(addrs, maxaddrs, count); +#else + _status = stun_getifaddrs(addrs, maxaddrs, count); +#endif + + if ((r=nr_stun_remove_duplicate_addrs(addrs, drop_loopback, drop_link_local, count))) + ABORT(r); + + for (i = 0; i < *count; ++i) { + nr_local_addr_fmt_info_string(addrs+i,typestr,sizeof(typestr)); + r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s, type: %s\n", + i,addrs[i].addr.as_string,addrs[i].addr.ifname,typestr); + } + +abort: + return _status; +} + +#endif diff --git a/media/mtransport/third_party/nICEr/src/stun/addrs.h b/media/mtransport/third_party/nICEr/src/stun/addrs.h new file mode 100644 index 000000000..61a3496d1 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/addrs.h @@ -0,0 +1,43 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _addrs_h_ +#define _addrs_h_ + +#include "transport_addr.h" +#include "local_addr.h" + +int nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int remove_loopback, int remove_link_local, int *count); +int nr_stun_remove_duplicate_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count); + +#endif diff --git a/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.c b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.c new file mode 100644 index 000000000..d7d390f24 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.c @@ -0,0 +1,242 @@ +/* +Copyright (c) 2011, The WebRTC project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if defined(ANDROID) +#include "ifaddrs-android.h" +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/utsname.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <net/if.h> +#include <unistd.h> +#include <errno.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +struct netlinkrequest { + struct nlmsghdr header; + struct ifaddrmsg msg; +}; + +static const int kMaxReadSize = 4096; + +static int set_ifname(struct ifaddrs* ifaddr, int interface) { + char buf[IFNAMSIZ] = {0}; + char* name = if_indextoname(interface, buf); + if (name == NULL) { + return -1; + } + ifaddr->ifa_name = malloc(strlen(name) + 1); + strncpy(ifaddr->ifa_name, name, strlen(name) + 1); + return 0; +} + +static int set_flags(struct ifaddrs* ifaddr) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + return -1; + } + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1); + int rc = ioctl(fd, SIOCGIFFLAGS, &ifr); + close(fd); + if (rc == -1) { + return -1; + } + ifaddr->ifa_flags = ifr.ifr_flags; + return 0; +} + +static int set_addresses(struct ifaddrs* ifaddr, struct ifaddrmsg* msg, void* data, + size_t len) { + if (msg->ifa_family == AF_INET) { + struct sockaddr_in* sa = malloc(sizeof(struct sockaddr_in)); + memset(sa, 0, sizeof(struct sockaddr_in)); + sa->sin_family = AF_INET; + memcpy(&sa->sin_addr, data, len); + ifaddr->ifa_addr = (struct sockaddr*)sa; + } else if (msg->ifa_family == AF_INET6) { + struct sockaddr_in6* sa = malloc(sizeof(struct sockaddr_in6)); + memset(sa, 0, sizeof(struct sockaddr_in6)); + sa->sin6_family = AF_INET6; + sa->sin6_scope_id = msg->ifa_index; + memcpy(&sa->sin6_addr, data, len); + ifaddr->ifa_addr = (struct sockaddr*)sa; + } else { + return -1; + } + return 0; +} + +static int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) { + char* prefix = NULL; + if (family == AF_INET) { + struct sockaddr_in* mask = malloc(sizeof(struct sockaddr_in)); + memset(mask, 0, sizeof(struct sockaddr_in)); + mask->sin_family = AF_INET; + memset(&mask->sin_addr, 0, sizeof(struct in_addr)); + ifaddr->ifa_netmask = (struct sockaddr*)mask; + if (prefixlen > 32) { + prefixlen = 32; + } + prefix = (char*)&mask->sin_addr; + } else if (family == AF_INET6) { + struct sockaddr_in6* mask = malloc(sizeof(struct sockaddr_in6)); + memset(mask, 0, sizeof(struct sockaddr_in6)); + mask->sin6_family = AF_INET6; + memset(&mask->sin6_addr, 0, sizeof(struct in6_addr)); + ifaddr->ifa_netmask = (struct sockaddr*)mask; + if (prefixlen > 128) { + prefixlen = 128; + } + prefix = (char*)&mask->sin6_addr; + } else { + return -1; + } + for (int i = 0; i < (prefixlen / 8); i++) { + *prefix++ = 0xFF; + } + char remainder = 0xff; + remainder <<= (8 - prefixlen % 8); + *prefix = remainder; + return 0; +} + +static int populate_ifaddrs(struct ifaddrs* ifaddr, struct ifaddrmsg* msg, void* bytes, + size_t len) { + if (set_ifname(ifaddr, msg->ifa_index) != 0) { + return -1; + } + if (set_flags(ifaddr) != 0) { + return -1; + } + if (set_addresses(ifaddr, msg, bytes, len) != 0) { + return -1; + } + if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) { + return -1; + } + return 0; +} + +int android_getifaddrs(struct ifaddrs** result) { + int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { + return -1; + } + + struct netlinkrequest ifaddr_request; + memset(&ifaddr_request, 0, sizeof(ifaddr_request)); + ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; + ifaddr_request.header.nlmsg_type = RTM_GETADDR; + ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); + if ((size_t)count != ifaddr_request.header.nlmsg_len) { + close(fd); + return -1; + } + struct ifaddrs* start = NULL; + struct ifaddrs* current = NULL; + char buf[kMaxReadSize]; + ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); + while (amount_read > 0) { + struct nlmsghdr* header = (struct nlmsghdr*)&buf[0]; + size_t header_size = (size_t)amount_read; + for ( ; NLMSG_OK(header, header_size); + header = NLMSG_NEXT(header, header_size)) { + switch (header->nlmsg_type) { + case NLMSG_DONE: + /* Success. Return. */ + *result = start; + close(fd); + return 0; + case NLMSG_ERROR: + close(fd); + android_freeifaddrs(start); + return -1; + case RTM_NEWADDR: { + struct ifaddrmsg* address_msg = + (struct ifaddrmsg*)NLMSG_DATA(header); + struct rtattr* rta = IFA_RTA(address_msg); + ssize_t payload_len = IFA_PAYLOAD(header); + while (RTA_OK(rta, payload_len)) { + if (rta->rta_type == IFA_ADDRESS) { + int family = address_msg->ifa_family; + if (family == AF_INET || family == AF_INET6) { + struct ifaddrs* newest = malloc(sizeof(struct ifaddrs)); + memset(newest, 0, sizeof(struct ifaddrs)); + if (current) { + current->ifa_next = newest; + } else { + start = newest; + } + if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta), + RTA_PAYLOAD(rta)) != 0) { + android_freeifaddrs(start); + *result = NULL; + return -1; + } + current = newest; + } + } + rta = RTA_NEXT(rta, payload_len); + } + break; + } + } + } + amount_read = recv(fd, &buf, kMaxReadSize, 0); + } + close(fd); + android_freeifaddrs(start); + return -1; +} + +void android_freeifaddrs(struct ifaddrs* addrs) { + struct ifaddrs* last = NULL; + struct ifaddrs* cursor = addrs; + while (cursor) { + free(cursor->ifa_name); + free(cursor->ifa_addr); + free(cursor->ifa_netmask); + last = cursor; + cursor = cursor->ifa_next; + free(last); + } +} + +#endif /* defined(ANDROID) */ diff --git a/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.h b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.h new file mode 100644 index 000000000..5ba174ce6 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/ifaddrs-android.h @@ -0,0 +1,57 @@ +/* +Copyright (c) 2011, The WebRTC project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef WEBRTC_BASE_IFADDRS_ANDROID_H_ +#define WEBRTC_BASE_IFADDRS_ANDROID_H_ + +#include <stdio.h> +#include <sys/socket.h> + +/* Implementation of getifaddrs for Android. + * Fills out a list of ifaddr structs (see below) which contain information + * about every network interface available on the host. + * See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function). */ +struct ifaddrs { + struct ifaddrs* ifa_next; + char* ifa_name; + unsigned int ifa_flags; + struct sockaddr* ifa_addr; + struct sockaddr* ifa_netmask; + /* Real ifaddrs has broadcast, point to point and data members. + * We don't need them (yet?). */ +}; + +int android_getifaddrs(struct ifaddrs** result); +void android_freeifaddrs(struct ifaddrs* addrs); + +#endif /* WEBRTC_BASE_IFADDRS_ANDROID_H_ */ + diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c new file mode 100644 index 000000000..c82c545f3 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c @@ -0,0 +1,610 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <nr_api.h> + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <assert.h> +#include <inttypes.h> + +#include "p_buf.h" +#include "nr_socket.h" +#include "stun.h" +#include "nr_socket_buffered_stun.h" + +#define NR_MAX_FRAME_SIZE 0xFFFF + +typedef struct nr_frame_header_ { + UINT2 frame_length; + char data[0]; +} nr_frame_header; + +typedef struct nr_socket_buffered_stun_ { + nr_socket *inner; + nr_transport_addr remote_addr; + int connected; + + /* Read state */ + int read_state; +#define NR_ICE_SOCKET_READ_NONE 0 +#define NR_ICE_SOCKET_READ_HDR 1 +#define NR_ICE_SOCKET_READ_FAILED 2 + UCHAR *buffer; + size_t buffer_size; + size_t bytes_needed; + size_t bytes_read; + NR_async_cb readable_cb; + void *readable_cb_arg; + + /* Write state */ + nr_p_buf_ctx *p_bufs; + nr_p_buf_head pending_writes; + size_t pending; + size_t max_pending; + nr_framing_type framing_type; +} nr_socket_buffered_stun; + +static int nr_socket_buffered_stun_destroy(void **objp); +static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len, + int flags, nr_transport_addr *to); +static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from); +static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd); +static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp); +static int nr_socket_buffered_stun_close(void *obj); +static int nr_socket_buffered_stun_connect(void *sock, nr_transport_addr *addr); +static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written); +static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg); +static int nr_socket_buffered_stun_listen(void *obj, int backlog); +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp); + +static nr_socket_vtbl nr_socket_buffered_stun_vtbl={ + 2, + nr_socket_buffered_stun_destroy, + nr_socket_buffered_stun_sendto, + nr_socket_buffered_stun_recvfrom, + nr_socket_buffered_stun_getfd, + nr_socket_buffered_stun_getaddr, + nr_socket_buffered_stun_connect, + 0, + 0, + nr_socket_buffered_stun_close, + nr_socket_buffered_stun_listen, + nr_socket_buffered_stun_accept +}; + +void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg) +{ + nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj; + + buf_sock->readable_cb = readable_cb; + buf_sock->readable_cb_arg = readable_cb_arg; +} + +int nr_socket_buffered_set_connected_to(nr_socket *sock, nr_transport_addr *remote_addr) +{ + nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj; + int r, _status; + + if ((r=nr_transport_addr_copy(&buf_sock->remote_addr, remote_addr))) + ABORT(r); + + buf_sock->connected = 1; + + _status=0; +abort: + return(_status); +} + +int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, + nr_framing_type framing_type, nr_socket **sockp) +{ + int r, _status; + nr_socket_buffered_stun *sock = 0; + size_t frame_size; + + if (!(sock = RCALLOC(sizeof(nr_socket_buffered_stun)))) + ABORT(R_NO_MEMORY); + + sock->inner = inner; + sock->framing_type = framing_type; + + if ((r=nr_ip4_port_to_transport_addr(INADDR_ANY, 0, IPPROTO_UDP, &sock->remote_addr))) + ABORT(r); + + switch (framing_type) { + case ICE_TCP_FRAMING: + frame_size = sizeof(nr_frame_header); + sock->buffer_size = sizeof(nr_frame_header) + NR_MAX_FRAME_SIZE; + sock->bytes_needed = sizeof(nr_frame_header); + break; + case TURN_TCP_FRAMING: + frame_size = 0; + sock->buffer_size = NR_STUN_MAX_MESSAGE_SIZE; + sock->bytes_needed = sizeof(nr_stun_message_header); + break; + default: + assert(0); + ABORT(R_BAD_ARGS); + } + + /* TODO(ekr@rtfm.com): Check this */ + if (!(sock->buffer = RMALLOC(sock->buffer_size))) + ABORT(R_NO_MEMORY); + + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->connected = 0; + + STAILQ_INIT(&sock->pending_writes); + if ((r=nr_p_buf_ctx_create(sock->buffer_size, &sock->p_bufs))) + ABORT(r); + sock->max_pending = max_pending + frame_size; + + if ((r=nr_socket_create_int(sock, &nr_socket_buffered_stun_vtbl, sockp))) + ABORT(r); + + _status=0; +abort: + if (_status && sock) { + void *sock_v = sock; + sock->inner = 0; /* Give up ownership so we don't destroy */ + nr_socket_buffered_stun_destroy(&sock_v); + } + return(_status); +} + +/* Note: This destroys the inner socket */ +int nr_socket_buffered_stun_destroy(void **objp) +{ + nr_socket_buffered_stun *sock; + NR_SOCKET fd; + + if (!objp || !*objp) + return 0; + + sock = (nr_socket_buffered_stun *)*objp; + *objp = 0; + + /* Free the buffer if needed */ + RFREE(sock->buffer); + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + } + + nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes); + nr_p_buf_ctx_destroy(&sock->p_bufs); + nr_socket_destroy(&sock->inner); + RFREE(sock); + + return 0; +} + +static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len, + int flags, nr_transport_addr *to) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + int r, _status; + size_t written; + nr_frame_header *frame = NULL; + + /* Check that we are writing to the connected address if + connected */ + if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) { + if (nr_transport_addr_cmp(&sock->remote_addr, to, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(LOG_GENERIC, LOG_ERR, "Sendto on connected socket doesn't match"); + ABORT(R_BAD_DATA); + } + } + + if (sock->framing_type == ICE_TCP_FRAMING) { + + assert(len <= NR_MAX_FRAME_SIZE); + if (len > NR_MAX_FRAME_SIZE) + ABORT(R_FAILED); + + if (!(frame = RMALLOC(len + sizeof(nr_frame_header)))) + ABORT(R_NO_MEMORY); + + frame->frame_length = htons(len); + memcpy(frame->data, msg, len); + len += sizeof(nr_frame_header); + msg = frame; + } + + if ((r=nr_socket_buffered_stun_write(obj, msg, len, &written))) + ABORT(r); + + if (len != written) + ABORT(R_IO_ERROR); + + _status=0; +abort: + RFREE(frame); + return _status; +} + +static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock) + { + NR_SOCKET fd; + + sock->read_state = NR_ICE_SOCKET_READ_FAILED; + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + } + +static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from) +{ + int r, _status; + size_t bytes_read; + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + nr_frame_header *frame = (nr_frame_header *)sock->buffer; + size_t skip_hdr_size = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : 0; + + if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) { + ABORT(R_FAILED); + } + + while (sock->bytes_needed) { + /* Read all the expected bytes */ + assert(sock->bytes_needed <= sock->buffer_size - sock->bytes_read); + + if(r=nr_socket_read(sock->inner, + sock->buffer + sock->bytes_read, + sock->bytes_needed, &bytes_read, 0)) + ABORT(r); + + assert(bytes_read <= sock->bytes_needed); + sock->bytes_needed -= bytes_read; + sock->bytes_read += bytes_read; + + /* Unfinished */ + if (sock->bytes_needed) + ABORT(R_WOULDBLOCK); + + /* No more bytes expected */ + if (sock->read_state == NR_ICE_SOCKET_READ_NONE) { + size_t remaining_length; + if (sock->framing_type == ICE_TCP_FRAMING) { + if (sock->bytes_read < sizeof(nr_frame_header)) + ABORT(R_BAD_DATA); + remaining_length = ntohs(frame->frame_length); + } else { + int tmp_length; + + /* Parse the header */ + if (r = nr_stun_message_length(sock->buffer, sock->bytes_read, &tmp_length)) + ABORT(r); + assert(tmp_length >= 0); + if (tmp_length < 0) + ABORT(R_BAD_DATA); + remaining_length = tmp_length; + + } + /* Check to see if we have enough room */ + if ((sock->buffer_size - sock->bytes_read) < remaining_length) + ABORT(R_BAD_DATA); + + sock->read_state = NR_ICE_SOCKET_READ_HDR; + /* Set ourselves up to read the rest of the data */ + sock->bytes_needed = remaining_length; + } + } + + assert(skip_hdr_size <= sock->bytes_read); + if (skip_hdr_size > sock->bytes_read) + ABORT(R_BAD_DATA); + sock->bytes_read -= skip_hdr_size; + + if (maxlen < sock->bytes_read) + ABORT(R_BAD_ARGS); + + *len = sock->bytes_read; + memcpy(buf, sock->buffer + skip_hdr_size, sock->bytes_read); + + sock->bytes_read = 0; + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->bytes_needed = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : sizeof(nr_stun_message_header); + + assert(!nr_transport_addr_is_wildcard(&sock->remote_addr)); + if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) { + if ((r=nr_transport_addr_copy(from, &sock->remote_addr))) + ABORT(r); + } + + _status=0; +abort: + if (_status && (_status != R_WOULDBLOCK)) { + nr_socket_buffered_stun_failed(sock); + } + + return(_status); +} + +static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + return nr_socket_getfd(sock->inner, fd); +} + +static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + return nr_socket_getaddr(sock->inner, addrp); +} + +static int nr_socket_buffered_stun_close(void *obj) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + NR_SOCKET fd; + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + } + + return nr_socket_close(sock->inner); +} + +static int nr_socket_buffered_stun_listen(void *obj, int backlog) +{ + int r, _status; + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + if (!sock->inner) + ABORT(R_FAILED); + + if ((r=nr_socket_listen(sock->inner, backlog))) + ABORT(r); + + _status=0; +abort: + return(_status); +} + + +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp) +{ + nr_socket_buffered_stun *bsock = (nr_socket_buffered_stun *)obj; + + return nr_socket_accept(bsock->inner, addrp, sockp); +} + +static void nr_socket_buffered_stun_connected_cb(NR_SOCKET s, int how, void *arg) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg; + int r, _status; + NR_SOCKET fd; + + assert(!sock->connected); + + sock->connected = 1; + + if ((r=nr_socket_getfd(sock->inner, &fd))) + ABORT(r); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + + // once connected arm for read + if (sock->readable_cb) { + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, sock->readable_cb, sock->readable_cb_arg); + } + + if (sock->pending) { + r_log(LOG_GENERIC, LOG_INFO, "Invoking writable_cb on connected (%u)", (uint32_t) sock->pending); + nr_socket_buffered_stun_writable_cb(s, how, arg); + } + + _status=0; +abort: + if (_status) { + r_log(LOG_GENERIC, LOG_ERR, "Failure in nr_socket_buffered_stun_connected_cb: %d", _status); + + } +} + +static int nr_socket_buffered_stun_connect(void *obj, nr_transport_addr *addr) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + int r, _status; + + if ((r=nr_transport_addr_copy(&sock->remote_addr, addr))) + ABORT(r); + + if ((r=nr_socket_connect(sock->inner, addr))) { + if (r == R_WOULDBLOCK) { + NR_SOCKET fd; + + if ((r=nr_socket_getfd(sock->inner, &fd))) + ABORT(r); + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_connected_cb, sock); + ABORT(R_WOULDBLOCK); + } + ABORT(r); + } else { + r_log(LOG_GENERIC, LOG_INFO, "Connected without blocking"); + sock->connected = 1; + } + + _status=0; +abort: + return(_status); +} + +static int nr_socket_buffered_stun_arm_writable_cb(nr_socket_buffered_stun *sock) +{ + int r, _status; + NR_SOCKET fd; + + if ((r=nr_socket_getfd(sock->inner, &fd))) + ABORT(r); + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_writable_cb, sock); + + _status=0; +abort: + return(_status); +} + +static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + int already_armed = 0; + int r,_status; + size_t written2 = 0; + size_t original_len = len; + + /* Buffers are close to full, report error. Do this now so we never + get partial writes */ + if ((sock->pending + len) > sock->max_pending) { + r_log(LOG_GENERIC, LOG_INFO, "Write buffer for %s full (%u + %u > %u) - re-arming @%p", + sock->remote_addr.as_string, (uint32_t)sock->pending, (uint32_t)len, (uint32_t)sock->max_pending, + &(sock->pending)); + ABORT(R_WOULDBLOCK); + } + + + if (sock->connected && !sock->pending) { + r = nr_socket_write(sock->inner, msg, len, &written2, 0); + if (r) { + if (r != R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d", + sock->remote_addr.as_string, r); + ABORT(r); + } + r_log(LOG_GENERIC, LOG_INFO, "Write of %" PRIu64 " blocked for %s", + (uint64_t) len, sock->remote_addr.as_string); + + written2=0; + } + } else { + already_armed = 1; + } + + /* Buffer what's left */ + len -= written2; + + if (len) { + if ((r=nr_p_buf_write_to_chain(sock->p_bufs, &sock->pending_writes, + ((UCHAR *)msg) + written2, len))) { + r_log(LOG_GENERIC, LOG_ERR, "Write_to_chain error for %s - %d", + sock->remote_addr.as_string, r); + + ABORT(r); + } + + sock->pending += len; + } + + if (sock->pending) { + if (!already_armed) { + if ((r=nr_socket_buffered_stun_arm_writable_cb(sock))) + ABORT(r); + } + r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s %u - %s armed (@%p),%s connected", + sock->remote_addr.as_string, (uint32_t)sock->pending, + already_armed ? "already" : "", &sock->pending, + sock->connected ? "" : " not"); + } + + *written = original_len; + + _status=0; +abort: + return _status; +} + +static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg; + int r,_status; + nr_p_buf *n1, *n2; + + if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) { + ABORT(R_FAILED); + } + + /* Try to flush */ + STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) { + size_t written = 0; + + if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset, + n1->length - n1->r_offset, + &written, 0))) { + + r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d", + sock->remote_addr.as_string, r); + ABORT(r); + } + + n1->r_offset += written; + assert(sock->pending >= written); + sock->pending -= written; + + if (n1->r_offset < n1->length) { + /* We wrote something, but not everything */ + r_log(LOG_GENERIC, LOG_INFO, "Write in callback didn't write all (remaining %u of %u) for %s", + n1->length - n1->r_offset, n1->length, + sock->remote_addr.as_string); + ABORT(R_WOULDBLOCK); + } + + /* We are done with this p_buf */ + STAILQ_REMOVE_HEAD(&sock->pending_writes, entry); + nr_p_buf_free(sock->p_bufs, n1); + } + + assert(!sock->pending); + _status=0; +abort: + r_log(LOG_GENERIC, LOG_INFO, "Writable_cb %s (%u (%p) pending)", + sock->remote_addr.as_string, (uint32_t)sock->pending, &(sock->pending)); + if (_status && _status != R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_ERR, "Failure in writable_cb: %d", _status); + nr_socket_buffered_stun_failed(sock); + } else if (sock->pending) { + nr_socket_buffered_stun_arm_writable_cb(sock); + } +} diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h new file mode 100644 index 000000000..fa65e4b08 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h @@ -0,0 +1,64 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _nr_socket_buffered_stun_h +#define _nr_socket_buffered_stun_h + +#include "nr_socket.h" + +/* Wrapper socket which provides buffered STUN-oriented I/O + + 1. Writes don't block and are automatically flushed when needed. + 2. All reads are in units of STUN messages + + This socket takes ownership of the inner socket |sock|. + */ + +typedef enum { + TURN_TCP_FRAMING=0, + ICE_TCP_FRAMING +} nr_framing_type; + +void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg); + +int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, + nr_framing_type framing_type, nr_socket **sockp); + +int nr_socket_buffered_set_connected_to(nr_socket *sock, + nr_transport_addr *remote_addr); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c new file mode 100644 index 000000000..a12966625 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.c @@ -0,0 +1,198 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: nr_socket_turn.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#ifdef USE_TURN + +#include <csi_platform.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <assert.h> + +#include "stun.h" +#include "turn_client_ctx.h" +#include "nr_socket_turn.h" + + +static char *nr_socket_turn_magic_cookie = "nr_socket_turn"; + +typedef struct nr_socket_turn_ { + char *magic_cookie; + nr_turn_client_ctx *turn; +} nr_socket_turn; + + +static int nr_socket_turn_destroy(void **objp); +static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len, + int flags, nr_transport_addr *to); +static int nr_socket_turn_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from); +static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd); +static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp); +static int nr_socket_turn_close(void *obj); + +static nr_socket_vtbl nr_socket_turn_vtbl={ + 2, + nr_socket_turn_destroy, + nr_socket_turn_sendto, + nr_socket_turn_recvfrom, + nr_socket_turn_getfd, + nr_socket_turn_getaddr, + 0, + 0, + 0, + nr_socket_turn_close, + 0, + 0 +}; + +int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp) + { + int r,_status; + nr_socket_turn *sturn=0; + + if(!(sturn=RCALLOC(sizeof(nr_socket_turn)))) + ABORT(R_NO_MEMORY); + + sturn->magic_cookie = nr_socket_turn_magic_cookie; + + if(r=nr_socket_create_int(sturn, &nr_socket_turn_vtbl, sockp)) + ABORT(r); + + _status=0; + abort: + if(_status){ + nr_socket_turn_destroy((void **)&sturn); + } + return(_status); + } + +static int nr_socket_turn_destroy(void **objp) + { + int _status; + nr_socket_turn *sturn; + + if(!objp || !*objp) + return(0); + + sturn=*objp; + *objp=0; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + + /* we don't own the socket, so don't destroy it */ + + RFREE(sturn); + + _status=0; + return(_status); + } + +static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len, + int flags, nr_transport_addr *addr) + { + int r,_status; + nr_socket_turn *sturn=obj; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(sturn->turn); + + if ((r = nr_turn_client_send_indication(sturn->turn, msg, len, flags, + addr))) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +static int nr_socket_turn_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *addr) + { + /* Reading from TURN sockets is done by the indication + processing code in turn_client_ctx. */ + assert(0); + + return(R_INTERNAL); + } + +static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd) + { + /* You should never directly be touching this fd. */ + assert(0); + + return(R_INTERNAL); + } + +static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp) + { + nr_socket_turn *sturn=obj; + int r, _status; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(sturn->turn); + + /* This returns the relayed address */ + if ((r=nr_turn_client_get_relayed_address(sturn->turn, addrp))) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +static int nr_socket_turn_close(void *obj) + { + /* No-op */ +#ifndef NDEBUG + nr_socket_turn *sturn=obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); +#endif + + return 0; + } + +int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx) +{ + nr_socket_turn *sturn=(nr_socket_turn*)sock->obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(!sturn->turn); + + sturn->turn = ctx; + + return 0; +} + +#endif /* USE_TURN */ diff --git a/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h new file mode 100644 index 000000000..c9d36d333 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_turn.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _nr_socket_turn_h +#define _nr_socket_turn_h + +#include "nr_socket.h" + +/* This is a partial implementation of an nr_socket wrapped + around TURN. It implements only the nr_socket features + actually used by the ICE stack. You can't, for instance, + read off the socket */ +int nr_socket_turn_create(nr_socket *sock, nr_socket **sockp); +int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun.h b/media/mtransport/third_party/nICEr/src/stun/stun.h new file mode 100644 index 000000000..8e5a60750 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun.h @@ -0,0 +1,216 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _STUN_H +#define _STUN_H + +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/param.h> +#include <sys/socket.h> +#ifndef LINUX +#include <net/if.h> +#if !defined(__OpenBSD__) && !defined(__NetBSD__) +#include <net/if_var.h> +#endif +#include <net/if_dl.h> +#include <net/if_types.h> +#else +#include <linux/if.h> +#endif +#ifndef BSD +#include <net/route.h> +#endif +#include <netinet/in.h> +#ifndef LINUX +#include <netinet/in_var.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> +#endif +#include <time.h> + +#include "nr_api.h" +#include "stun_msg.h" +#include "stun_build.h" +#include "stun_codec.h" +#include "stun_hint.h" +#include "stun_util.h" +#include "nr_socket.h" +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "stun_proc.h" + +#define NR_STUN_VERSION "rfc3489bis-11" +#define NR_STUN_PORT 3478 + +/* STUN attributes */ +#define NR_STUN_ATTR_MAPPED_ADDRESS 0x0001 +#define NR_STUN_ATTR_USERNAME 0x0006 +#define NR_STUN_ATTR_MESSAGE_INTEGRITY 0x0008 +#define NR_STUN_ATTR_ERROR_CODE 0x0009 +#define NR_STUN_ATTR_UNKNOWN_ATTRIBUTES 0x000A +#define NR_STUN_ATTR_REALM 0x0014 +#define NR_STUN_ATTR_NONCE 0x0015 +#define NR_STUN_ATTR_XOR_MAPPED_ADDRESS 0x0020 +#define NR_STUN_ATTR_SERVER 0x8022 +#define NR_STUN_ATTR_ALTERNATE_SERVER 0x8023 +#define NR_STUN_ATTR_FINGERPRINT 0x8028 + +/* for backwards compatibility with obsolete versions of the STUN spec */ +#define NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS 0x8020 + +#ifdef USE_STUND_0_96 +#define NR_STUN_ATTR_OLD_CHANGE_REQUEST 0x0003 +#endif /* USE_STUND_0_96 */ + +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE +/* for backwards compatibility with obsolete versions of the STUN spec */ +#define NR_STUN_ATTR_OLD_PASSWORD 0x0007 +#define NR_STUN_ATTR_OLD_RESPONSE_ADDRESS 0x0002 +#define NR_STUN_ATTR_OLD_SOURCE_ADDRESS 0x0004 +#define NR_STUN_ATTR_OLD_CHANGED_ADDRESS 0x0005 +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + +#ifdef USE_ICE +/* ICE attributes */ +#define NR_STUN_ATTR_PRIORITY 0x0024 +#define NR_STUN_ATTR_USE_CANDIDATE 0x0025 +#define NR_STUN_ATTR_ICE_CONTROLLED 0x8029 +#define NR_STUN_ATTR_ICE_CONTROLLING 0x802A +#endif /* USE_ICE */ + +#ifdef USE_TURN +/* TURN attributes */ +#define NR_STUN_ATTR_LIFETIME 0x000d +#define NR_STUN_ATTR_XOR_PEER_ADDRESS 0x0012 +#define NR_STUN_ATTR_DATA 0x0013 +#define NR_STUN_ATTR_XOR_RELAY_ADDRESS 0x0016 +#define NR_STUN_ATTR_REQUESTED_TRANSPORT 0x0019 + +#define NR_STUN_ATTR_REQUESTED_TRANSPORT_UDP 17 +#endif /* USE_TURN */ + +/* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |M|M|M|M|M|C|M|M|M|C|M|M|M|M| + * |1|1|9|8|7|1|6|5|4|0|3|2|1|0| + * |1|0| | | | | | | | | | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Figure 3: Format of STUN Message Type Field + */ +#define NR_STUN_METHOD_TYPE_BITS(m) \ + ((((m) & 0xf80) << 2) | (((m) & 0x070) << 1) | ((m) & 0x00f)) + +#define NR_STUN_CLASS_TYPE_BITS(c) \ + ((((c) & 0x002) << 7) | (((c) & 0x001) << 4)) + +#define NR_STUN_GET_TYPE_METHOD(t) \ + ((((t) >> 2) & 0xf80) | (((t) >> 1) & 0x070) | ((t) & 0x00f)) + +#define NR_STUN_GET_TYPE_CLASS(t) \ + ((((t) >> 7) & 0x002) | (((t) >> 4) & 0x001)) + +#define NR_STUN_TYPE(m,c) (NR_STUN_METHOD_TYPE_BITS((m)) | NR_STUN_CLASS_TYPE_BITS((c))) + +/* building blocks for message types */ +#define NR_METHOD_BINDING 0x001 +#define NR_CLASS_REQUEST 0x0 +#define NR_CLASS_INDICATION 0x1 +#define NR_CLASS_RESPONSE 0x2 +#define NR_CLASS_ERROR_RESPONSE 0x3 + +/* define types for STUN messages */ +#define NR_STUN_MSG_BINDING_REQUEST NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_BINDING_INDICATION NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_INDICATION) +#define NR_STUN_MSG_BINDING_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_BINDING_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_ERROR_RESPONSE) + +#ifdef USE_TURN +/* building blocks for TURN message types */ +#define NR_METHOD_ALLOCATE 0x003 +#define NR_METHOD_REFRESH 0x004 + +#define NR_METHOD_SEND 0x006 +#define NR_METHOD_DATA 0x007 +#define NR_METHOD_CREATE_PERMISSION 0x008 +#define NR_METHOD_CHANNEL_BIND 0x009 + +/* define types for a TURN message */ +#define NR_STUN_MSG_ALLOCATE_REQUEST NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_ALLOCATE_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_ERROR_RESPONSE) +#define NR_STUN_MSG_REFRESH_REQUEST NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_REFRESH_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_REFRESH_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_ERROR_RESPONSE) + +#define NR_STUN_MSG_SEND_INDICATION NR_STUN_TYPE(NR_METHOD_SEND, \ + NR_CLASS_INDICATION) +#define NR_STUN_MSG_DATA_INDICATION NR_STUN_TYPE(NR_METHOD_DATA, \ + NR_CLASS_INDICATION) + +#define NR_STUN_MSG_PERMISSION_REQUEST NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_PERMISSION_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_PERMISSION_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_ERROR_RESPONSE) + +#define NR_STUN_MSG_CHANNEL_BIND_REQUEST NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_CHANNEL_BIND_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_ERROR_RESPONSE) + + +#endif /* USE_TURN */ + + +#define NR_STUN_AUTH_RULE_OPTIONAL (1<<0) +#define NR_STUN_AUTH_RULE_SHORT_TERM (1<<8) +#define NR_STUN_AUTH_RULE_LONG_TERM (1<<9) + +#endif diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_build.c b/media/mtransport/third_party/nICEr/src/stun/stun_build.c new file mode 100644 index 000000000..b029501a7 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_build.c @@ -0,0 +1,614 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: stun_build.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <csi_platform.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "nr_api.h" +#include "stun.h" +#include "registry.h" +#include "stun_reg.h" +#include "nr_crypto.h" + + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.1 */ +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.1 */ +/* note that S 10.1.1 states the message MUST include MESSAGE-INTEGRITY + * and USERNAME, but that's not correct -- for instance ICE keepalive + * messages don't include these (See draft-ietf-mmusic-ice-18.txt S 10: + * "If STUN is being used for keepalives, a STUN Binding Indication is + * used. The Indication MUST NOT utilize any authentication mechanism") + */ +int +nr_stun_form_request_or_indication(int mode, int msg_type, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + assert(NR_STUN_GET_TYPE_CLASS(msg_type) == NR_CLASS_REQUEST + || NR_STUN_GET_TYPE_CLASS(msg_type) == NR_CLASS_INDICATION); + + *msg = 0; + + if ((r=nr_stun_message_create(&req))) + ABORT(r); + + req->header.type = msg_type; + + nr_crypto_random_bytes((UCHAR*)&req->header.id,sizeof(req->header.id)); + + switch (mode) { + default: + if ((r=nr_stun_message_add_fingerprint_attribute(req))) + ABORT(r); + /* fall through */ + case NR_STUN_MODE_STUN_NO_AUTH: + req->header.magic_cookie = NR_STUN_MAGIC_COOKIE; + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MODE_STUND_0_96: + req->header.magic_cookie = NR_STUN_MAGIC_COOKIE2; + + /* actually, stund 0.96 just ignores the fingerprint + * attribute, but don't bother to send it */ + + break; +#endif /* USE_STUND_0_96 */ + + } + + *msg = req; + + _status=0; + abort: + if (_status) RFREE(req); + return _status; +} + +int +nr_stun_build_req_lt_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_username_attribute(req, params->username))) + ABORT(r); + + if (params->realm && params->nonce) { + if ((r=nr_stun_message_add_realm_attribute(req, params->realm))) + ABORT(r); + + if ((r=nr_stun_message_add_nonce_attribute(req, params->nonce))) + ABORT(r); + + if ((r=nr_stun_message_add_message_integrity_attribute(req, params->password))) + ABORT(r); + } + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_req_st_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_username_attribute(req, params->username))) + ABORT(r); + + if (params->password) { + if ((r=nr_stun_message_add_message_integrity_attribute(req, params->password))) + ABORT(r); + } + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_req_no_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN_NO_AUTH, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + +int +nr_stun_build_keepalive(nr_stun_client_stun_keepalive_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *ind = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_INDICATION, &ind))) + ABORT(r); + + *msg = ind; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&ind); + return _status; +} + +#ifdef USE_STUND_0_96 +int +nr_stun_build_req_stund_0_96(nr_stun_client_stun_binding_request_stund_0_96_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUND_0_96, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_change_request_attribute(req, 0))) + ABORT(r); + + assert(! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)); + assert(! nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE +int +nr_stun_build_use_candidate(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_BINDING_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_username_attribute(req, params->username))) + ABORT(r); + + if ((r=nr_stun_message_add_message_integrity_attribute(req, ¶ms->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, ¶ms->password))) + ABORT(r); + + if ((r=nr_stun_message_add_priority_attribute(req, params->priority))) + ABORT(r); + + switch (params->control) { + case NR_ICE_CONTROLLING: + if ((r=nr_stun_message_add_ice_controlling_attribute(req, params->tiebreaker))) + ABORT(r); + break; + case NR_ICE_CONTROLLED: + if ((r=nr_stun_message_add_ice_controlled_attribute(req, params->tiebreaker))) + ABORT(r); + break; + default: + assert(0); + ABORT(R_INTERNAL); + } + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} +#endif /* USE_ICE */ + +#ifdef USE_TURN + +#ifndef __isascii +#define __isascii(c) (((c) & ~0x7F) == 0) +#endif + +/* Long-term passwords are computed over the key: + + key = MD5(username ":" realm ":" SASLprep(password)) + + Per RFC 5389 S 15.4 +*/ +int +nr_stun_compute_lt_message_integrity_password(const char *username, const char *realm, + Data *password, Data *hmac_key) +{ + char digest_input[1000]; + int i; + int r, _status; + size_t len; + + /* First check that the password is ASCII. We are supposed to + SASLprep but we don't support this yet + TODO(ekr@rtfm.com): Add SASLprep for password. + */ + for (i=0; i<password->len; i++) { + if (!__isascii(password->data[i])) + ABORT(R_BAD_DATA); + } + + if (hmac_key->len < 16) + ABORT(R_BAD_ARGS); + + snprintf(digest_input, sizeof(digest_input), "%s:%s:", username, realm); + if ((sizeof(digest_input) - strlen(digest_input)) < password->len) + ABORT(R_BAD_DATA); + + len = strlen(digest_input); + memcpy(digest_input + len, password->data, password->len); + + + if (r=nr_crypto_md5((UCHAR *)digest_input, len + password->len, hmac_key->data)) + ABORT(r); + hmac_key->len=16; + + _status=0; +abort: + return(_status); +} + +static int +nr_stun_build_auth_params(nr_stun_client_auth_params *auth, nr_stun_message *req) +{ + int r, _status; + UCHAR hmac_key_d[16]; + Data hmac_key; + + ATTACH_DATA(hmac_key, hmac_key_d); + + if (!auth->authenticate) + goto done; + + assert(auth->username); + assert(auth->password.len); + assert(auth->realm); + assert(auth->nonce); + + if (r=nr_stun_compute_lt_message_integrity_password(auth->username, + auth->realm, + &auth->password, + &hmac_key)) + ABORT(r); + + if (!auth->username) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no username provided"); + ABORT(R_INTERNAL); + } + + if (!auth->password.len) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no password provided"); + ABORT(R_INTERNAL); + } + + if (!auth->realm) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no realm provided"); + ABORT(R_INTERNAL); + } + + if (!auth->nonce) { + r_log(NR_LOG_STUN, LOG_WARNING, "STUN authentication requested but no nonce provided"); + ABORT(R_INTERNAL); + } + + if ((r=nr_stun_message_add_username_attribute(req, auth->username))) + ABORT(r); + + if ((r=nr_stun_message_add_realm_attribute(req, auth->realm))) + ABORT(r); + + if ((r=nr_stun_message_add_nonce_attribute(req, auth->nonce))) + ABORT(r); + + if ((r=nr_stun_message_add_message_integrity_attribute(req, &hmac_key))) + ABORT(r); + +done: + _status=0; +abort: + return(_status); +} + +int +nr_stun_build_allocate_request(nr_stun_client_auth_params *auth, nr_stun_client_allocate_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_ALLOCATE_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_requested_transport_attribute(req, NR_STUN_ATTR_REQUESTED_TRANSPORT_UDP))) + ABORT(r); + + if ((r=nr_stun_message_add_lifetime_attribute(req, params->lifetime_secs))) + ABORT(r); + + /* TODO(ekr@rtfm.com): Add the SOFTWARE attribute (Firefox bug 857666) */ + + if ((r=nr_stun_build_auth_params(auth, req))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + + +int nr_stun_build_refresh_request(nr_stun_client_auth_params *auth, nr_stun_client_refresh_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_REFRESH_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_lifetime_attribute(req, params->lifetime_secs))) + ABORT(r); + + + /* TODO(ekr@rtfm.com): Add the SOFTWARE attribute (Firefox bug 857666) */ + + if ((r=nr_stun_build_auth_params(auth, req))) + ABORT(r); + + *msg = req; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&req); + return _status; +} + + +int nr_stun_build_permission_request(nr_stun_client_auth_params *auth, nr_stun_client_permission_request_params *params, nr_stun_message **msg) +{ + int r,_status; + nr_stun_message *req = 0; + + if ((r=nr_stun_form_request_or_indication(NR_STUN_MODE_STUN, NR_STUN_MSG_PERMISSION_REQUEST, &req))) + ABORT(r); + + if ((r=nr_stun_message_add_xor_peer_address_attribute(req, ¶ms->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, ¶ms->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, ¶ms->remote_addr))) + ABORT(r); + + if ((r=nr_stun_message_add_data_attribute(ind, params->data.data, params->data.len))) + ABORT(r); + + *msg = ind; + + _status=0; + abort: + if (_status) nr_stun_message_destroy(&ind); + return _status; +} + +#endif /* USE_TURN */ + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1.1 */ +int +nr_stun_form_success_response(nr_stun_message *req, nr_transport_addr *from, Data *password, nr_stun_message *res) +{ + int r,_status; + int request_method; + char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */ + + /* set up information for default response */ + + request_method = NR_STUN_GET_TYPE_METHOD(req->header.type); + res->header.type = NR_STUN_TYPE(request_method, NR_CLASS_RESPONSE); + res->header.magic_cookie = req->header.magic_cookie; + memcpy(&res->header.id, &req->header.id, sizeof(res->header.id)); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Mapped Address = %s", from->as_string); + + if ((r=nr_stun_message_add_xor_mapped_address_attribute(res, from))) + ABORT(r); + + if (!NR_reg_get_string(NR_STUN_REG_PREF_SERVER_NAME, server_name, sizeof(server_name))) { + if ((r=nr_stun_message_add_server_attribute(res, server_name))) + ABORT(r); + } + + if (res->header.magic_cookie == NR_STUN_MAGIC_COOKIE) { + if (password != 0) { + if ((r=nr_stun_message_add_message_integrity_attribute(res, password))) + ABORT(r); + } + + if ((r=nr_stun_message_add_fingerprint_attribute(res))) + ABORT(r); + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1.1 */ +void +nr_stun_form_error_response(nr_stun_message *req, nr_stun_message* res, int number, char* msg) +{ + char *str; + int request_method; + char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */ + + if (number < 300 || number > 699) + number = 500; + + r_log(NR_LOG_STUN, LOG_INFO, "Responding with error %d: %s", number, msg); + + request_method = NR_STUN_GET_TYPE_METHOD(req->header.type); + res->header.type = NR_STUN_TYPE(request_method, NR_CLASS_ERROR_RESPONSE); + res->header.magic_cookie = req->header.magic_cookie; + memcpy(&res->header.id, &req->header.id, sizeof(res->header.id)); + + /* during development we should never see 500s (hopefully not in deployment either) */ + + str = 0; + switch (number) { + case 300: str = "Try Alternate"; break; + case 400: str = "Bad Request"; break; + case 401: str = "Unauthorized"; break; + case 420: str = "Unknown Attribute"; break; + case 438: str = "Stale Nonce"; break; +#ifdef USE_ICE + case 487: str = "Role Conflict"; break; +#endif + case 500: str = "Server Error"; break; + } + if (str == 0) { + str = "Unknown"; + } + + if (nr_stun_message_add_error_code_attribute(res, number, str)) { + assert(0); /* should never happen */ + } + + if (!NR_reg_get_string(NR_STUN_REG_PREF_SERVER_NAME, server_name, sizeof(server_name))) { + nr_stun_message_add_server_attribute(res, server_name); + } +} + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_build.h b/media/mtransport/third_party/nICEr/src/stun/stun_build.h new file mode 100644 index 000000000..c3f91a87b --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_build.h @@ -0,0 +1,147 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _stun_build_h +#define _stun_build_h + +#include "stun.h" + +#define NR_STUN_MODE_STUN 1 +#ifdef USE_STUND_0_96 +#define NR_STUN_MODE_STUND_0_96 2 /* backwards compatibility mode */ +#endif /* USE_STUND_0_96 */ +#define NR_STUN_MODE_STUN_NO_AUTH 3 +int nr_stun_form_request_or_indication(int mode, int msg_type, nr_stun_message **msg); + +typedef struct nr_stun_client_stun_binding_request_params_ { + char *username; + Data *password; + char *nonce; + char *realm; +} nr_stun_client_stun_binding_request_params; + +int nr_stun_build_req_lt_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); +int nr_stun_build_req_st_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); +int nr_stun_build_req_no_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_stun_keepalive_params_ { +#if defined(WIN32) || defined(__clang__) + // VC++ and clang give error and warning respectively if no members + int dummy; +#endif +} nr_stun_client_stun_keepalive_params; + +int nr_stun_build_keepalive(nr_stun_client_stun_keepalive_params *params, nr_stun_message **msg); + + +#ifdef USE_STUND_0_96 +typedef struct nr_stun_client_stun_binding_request_stund_0_96_params_ { +#ifdef WIN32 // silly VC++ gives error if no members + int dummy; +#endif +} nr_stun_client_stun_binding_request_stund_0_96_params; + +int nr_stun_build_req_stund_0_96(nr_stun_client_stun_binding_request_stund_0_96_params *params, nr_stun_message **msg); +#endif /* USE_STUND_0_96 */ + + +#ifdef USE_ICE +typedef struct nr_stun_client_ice_binding_request_params_ { + char *username; + Data password; + UINT4 priority; + int control; +#define NR_ICE_CONTROLLING 1 +#define NR_ICE_CONTROLLED 2 + UINT8 tiebreaker; +} nr_stun_client_ice_binding_request_params; + +int nr_stun_build_use_candidate(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg); + +int nr_stun_build_req_ice(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg); +#endif /* USE_ICE */ + + +typedef struct nr_stun_client_auth_params_ { + char authenticate; + char *username; + char *realm; + char *nonce; + Data password; +} nr_stun_client_auth_params; + +#ifdef USE_TURN +typedef struct nr_stun_client_allocate_request_params_ { + UINT4 lifetime_secs; +} nr_stun_client_allocate_request_params; + +int nr_stun_build_allocate_request(nr_stun_client_auth_params *auth, nr_stun_client_allocate_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_refresh_request_params_ { + UINT4 lifetime_secs; +} nr_stun_client_refresh_request_params; + +int nr_stun_build_refresh_request(nr_stun_client_auth_params *auth, nr_stun_client_refresh_request_params *params, nr_stun_message **msg); + + + +typedef struct nr_stun_client_permission_request_params_ { + nr_transport_addr remote_addr; +} nr_stun_client_permission_request_params; + +int nr_stun_build_permission_request(nr_stun_client_auth_params *auth, nr_stun_client_permission_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_send_indication_params_ { + nr_transport_addr remote_addr; + Data data; +} nr_stun_client_send_indication_params; + +int nr_stun_build_send_indication(nr_stun_client_send_indication_params *params, nr_stun_message **msg); + +typedef struct nr_stun_client_data_indication_params_ { + nr_transport_addr remote_addr; + Data data; +} nr_stun_client_data_indication_params; + +int nr_stun_build_data_indication(nr_stun_client_data_indication_params *params, nr_stun_message **msg); +#endif /* USE_TURN */ + +int nr_stun_form_success_response(nr_stun_message *req, nr_transport_addr *from, Data *password, nr_stun_message *res); +void nr_stun_form_error_response(nr_stun_message *request, nr_stun_message* response, int number, char* msg); +int nr_stun_compute_lt_message_integrity_password(const char *username, const char *realm, + Data *password, Data *hmac_key); + +#endif diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c new file mode 100644 index 000000000..e8d91982b --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c @@ -0,0 +1,824 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: stun_client_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <assert.h> +#include <string.h> +#include <math.h> + +#include <nr_api.h> +#include "stun.h" +#include "async_timer.h" +#include "registry.h" +#include "stun_reg.h" +#include "nr_crypto.h" +#include "r_time.h" + +static int nr_stun_client_send_request(nr_stun_client_ctx *ctx); +static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg); +static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password); + +#define NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD 1 +#define NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK 2 + +int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx=0; + char allow_loopback; + int r,_status; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx)))) + ABORT(R_NO_MEMORY); + + ctx->state=NR_STUN_CLIENT_STATE_INITTED; + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + ctx->sock=sock; + + nr_socket_getaddr(sock,&ctx->my_addr); + nr_transport_addr_copy(&ctx->peer_addr,peer); + + if (RTO != 0) { + ctx->rto_ms = RTO; + } else if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) { + ctx->rto_ms = 100; + } + + if (NR_reg_get_double(NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF, &ctx->retransmission_backoff_factor)) + ctx->retransmission_backoff_factor = 2.0; + + if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS, &ctx->maximum_transmits)) + ctx->maximum_transmits = 7; + + if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->maximum_transmits_timeout_ms)) + ctx->maximum_transmits_timeout_ms = 16 * ctx->rto_ms; + + ctx->mapped_addr_check_mask = NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD; + if (NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback) || + !allow_loopback) { + ctx->mapped_addr_check_mask |= NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK; + } + + if (ctx->my_addr.protocol == IPPROTO_TCP) { + /* Because TCP is reliable there is only one final timeout value. + * We store the timeout value for TCP in here, because timeout_ms gets + * reset to 0 in client_reset() which gets called from client_start() */ + ctx->maximum_transmits_timeout_ms = ctx->rto_ms * + pow(ctx->retransmission_backoff_factor, + ctx->maximum_transmits); + ctx->maximum_transmits = 1; + } + + *ctxp=ctx; + + _status=0; + abort: + if(_status){ + nr_stun_client_ctx_destroy(&ctx); + } + return(_status); + } + +static void nr_stun_client_fire_finished_cb(nr_stun_client_ctx *ctx) + { + if (ctx->finished_cb) { + NR_async_cb finished_cb = ctx->finished_cb; + ctx->finished_cb = 0; /* prevent 2nd call */ + /* finished_cb call must be absolutely last thing in function + * because as a side effect this ctx may be operated on in the + * callback */ + finished_cb(0,0,ctx->cb_arg); + } + } + +int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg) + { + int r,_status; + + if (ctx->state != NR_STUN_CLIENT_STATE_INITTED) + ABORT(R_NOT_PERMITTED); + + ctx->mode=mode; + + ctx->state=NR_STUN_CLIENT_STATE_RUNNING; + ctx->finished_cb=finished_cb; + ctx->cb_arg=cb_arg; + + if(mode!=NR_STUN_CLIENT_MODE_KEEPALIVE){ + if(r=nr_stun_client_send_request(ctx)) + ABORT(r); + } + + _status=0; + abort: + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { + nr_stun_client_fire_finished_cb(ctx); + } + + return(_status); + } + +int nr_stun_client_restart(nr_stun_client_ctx *ctx) + { + int r,_status; + int mode; + NR_async_cb finished_cb; + void *cb_arg; + nr_stun_message_attribute *ec; + nr_stun_message_attribute *as; + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + assert(ctx->retry_ct <= 2); + if (ctx->retry_ct > 2) + ABORT(R_NOT_PERMITTED); + + ++ctx->retry_ct; + + mode = ctx->mode; + finished_cb = ctx->finished_cb; + cb_arg = ctx->cb_arg; + + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_ERROR_CODE, &ec) + && ec->u.error_code.number == 300) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_ALTERNATE_SERVER, &as)) { + nr_transport_addr_copy(&ctx->peer_addr, &as->u.alternate_server); + } + } + + nr_stun_client_reset(ctx); + + if (r=nr_stun_client_start(ctx, mode, finished_cb, cb_arg)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int +nr_stun_client_reset(nr_stun_client_ctx *ctx) +{ + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + nr_stun_message_destroy(&ctx->request); + ctx->request = 0; + + nr_stun_message_destroy(&ctx->response); + ctx->response = 0; + + memset(&ctx->results, 0, sizeof(ctx->results)); + + ctx->mode = 0; + ctx->finished_cb = 0; + ctx->cb_arg = 0; + ctx->request_ct = 0; + ctx->timeout_ms = 0; + + ctx->state = NR_STUN_CLIENT_STATE_INITTED; + + return 0; +} + +static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg) + { + int _status; + nr_stun_client_ctx *ctx=cb_arg; + struct timeval now; + INT8 ms_waited; + + /* Prevent this timer from being cancelled later */ + ctx->timer_handle=0; + + /* Shouldn't happen */ + if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED) + ABORT(R_REJECTED); + + gettimeofday(&now, 0); + if (r_timeval_diff_ms(&now, &ctx->timer_set, &ms_waited)) { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired",ctx->label); + } + else { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired (after %llu ms)",ctx->label, ms_waited); + } + + if (ctx->request_ct >= ctx->maximum_transmits) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Timed out",ctx->label); + ctx->state=NR_STUN_CLIENT_STATE_TIMED_OUT; + ABORT(R_FAILED); + } + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + /* as a side effect will reset the timer */ + nr_stun_client_send_request(ctx); + + _status = 0; + abort: + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + nr_stun_client_fire_finished_cb(ctx); + } + return; + } + +int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx) + { + int r,_status; + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + if (ctx->request_ct > ctx->maximum_transmits) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Too many retransmit attempts",ctx->label); + ABORT(R_FAILED); + } + + /* if there is a scheduled retransimt, get rid of the scheduled retransmit + * and retransmit immediately */ + if (ctx->timer_handle) { + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + + if (r=nr_stun_client_send_request(ctx)) + ABORT(r); + } + + _status=0; + abort: + + return(_status); + } + +static int nr_stun_client_send_request(nr_stun_client_ctx *ctx) + { + int r,_status; + char string[256]; + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Sending check request (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,ctx->peer_addr.as_string); + + if (ctx->request == 0) { + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + ctx->params.stun_binding_request.nonce = ctx->nonce; + ctx->params.stun_binding_request.realm = ctx->realm; + assert(0); + ABORT(R_INTERNAL); + /* TODO(ekr@rtfm.com): Need to implement long-term auth for binding + requests */ + if ((r=nr_stun_build_req_lt_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + if ((r=nr_stun_build_req_st_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + if ((r=nr_stun_build_req_no_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_KEEPALIVE: + if ((r=nr_stun_build_keepalive(&ctx->params.stun_keepalive, &ctx->request))) + ABORT(r); + break; +#ifdef USE_STUND_0_96 + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + if ((r=nr_stun_build_req_stund_0_96(&ctx->params.stun_binding_request_stund_0_96, &ctx->request))) + ABORT(r); + break; +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + if ((r=nr_stun_build_use_candidate(&ctx->params.ice_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + if ((r=nr_stun_build_req_ice(&ctx->params.ice_binding_request, &ctx->request))) + ABORT(r); + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + if ((r=nr_stun_build_allocate_request(&ctx->auth_params, &ctx->params.allocate_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + if ((r=nr_stun_build_refresh_request(&ctx->auth_params, &ctx->params.refresh_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + if ((r=nr_stun_build_permission_request(&ctx->auth_params, &ctx->params.permission_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_SEND_INDICATION: + if ((r=nr_stun_build_send_indication(&ctx->params.send_indication, &ctx->request))) + ABORT(r); + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + } + + if (ctx->request->length == 0) { + if ((r=nr_stun_encode_message(ctx->request))) + ABORT(r); + } + + snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Sending to %s ", ctx->label, ctx->peer_addr.as_string); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)ctx->request->buffer, ctx->request->length); + + assert(ctx->my_addr.protocol==ctx->peer_addr.protocol); + + if(r=nr_socket_sendto(ctx->sock, ctx->request->buffer, ctx->request->length, 0, &ctx->peer_addr)) { + if (r != R_WOULDBLOCK) { + ABORT(r); + } + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_socket_sendto blocked, treating as dropped packet",ctx->label); + } + + ctx->request_ct++; + + if (NR_STUN_GET_TYPE_CLASS(ctx->request->header.type) == NR_CLASS_INDICATION) { + /* no need to set the timer because indications don't receive a + * response */ + } + else { + if (ctx->request_ct >= ctx->maximum_transmits) { + /* Reliable transport only get here once. Unreliable get here for + * their final timeout. */ + ctx->timeout_ms += ctx->maximum_transmits_timeout_ms; + } + else if (ctx->timeout_ms) { + /* exponential backoff */ + ctx->timeout_ms *= ctx->retransmission_backoff_factor; + } + else { + /* initial timeout unreliable transports */ + ctx->timeout_ms = ctx->rto_ms; + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Next timer will fire in %u ms",ctx->label, ctx->timeout_ms); + + gettimeofday(&ctx->timer_set, 0); + + assert(ctx->timeout_ms); + NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle); + } + + _status=0; + abort: + if (_status) { + nr_stun_client_failed(ctx); + } + return(_status); + } + +static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password) +{ + *password = (Data*)arg; + if (!arg) + return(R_NOT_FOUND); + return(0); +} + +int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 check) + { + if((check & NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD) && nr_transport_addr_is_wildcard(addr)) + return(R_BAD_DATA); + + if ((check & NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK) && nr_transport_addr_is_loopback(addr)) + return(R_BAD_DATA); + + return(0); + } + +int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr) + { + int r,_status; + char string[256]; + char *username = 0; + Data *password = 0; + nr_stun_message_attribute *attr; + nr_transport_addr *mapped_addr = 0; + int fail_on_error = 0; + UCHAR hmac_key_d[16]; + Data hmac_key; + int compute_lt_key=0; + /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */ + int response_matched=0; + + ATTACH_DATA(hmac_key, hmac_key_d); + + if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) && + (ctx->state != NR_STUN_CLIENT_STATE_WAITING)) + ABORT(R_REJECTED); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Inspecting STUN response (my_addr=%s, peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string); + + snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Received ", ctx->label); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len); + + /* determine password */ + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + compute_lt_key = 1; + /* Fall through */ + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + password = ctx->params.stun_binding_request.password; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + /* do nothing */ + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + /* do nothing */ + break; + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + password = &ctx->params.ice_binding_request.password; + break; + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + password = &ctx->params.ice_binding_request.password; + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_SEND_INDICATION: + /* do nothing -- we just got our DATA-INDICATION */ + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + + if (compute_lt_key) { + if (!ctx->realm || !username) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Long-term auth required but no realm/username specified. Randomizing key"); + /* Fill the key with random bytes to guarantee non-match */ + if (r=nr_crypto_random_bytes(hmac_key_d, sizeof(hmac_key_d))) + ABORT(r); + } + else { + if (r=nr_stun_compute_lt_message_integrity_password(username, ctx->realm, + password, &hmac_key)) + ABORT(r); + } + password = &hmac_key; + } + + if (ctx->response) { + nr_stun_message_destroy(&ctx->response); + } + + /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */ + if ((r=nr_stun_message_create2(&ctx->response, msg, len))) + ABORT(r); + + if ((r=nr_stun_decode_message(ctx->response, nr_stun_client_get_password, password))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): error decoding response",ctx->label); + ABORT(r); + } + + /* This will return an error if request and response don't match, + which is how we reject responses that match other contexts. */ + if ((r=nr_stun_receive_message(ctx->request, ctx->response))) { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Response is not for us",ctx->label); + ABORT(r); + } + + r_log(NR_LOG_STUN,LOG_INFO, + "STUN-CLIENT(%s): Received response; processing",ctx->label); + response_matched=1; + +/* TODO: !nn! currently using password!=0 to mean that auth is required, + * TODO: !nn! but we should probably pass that in explicitly via the + * TODO: !nn! usage (ctx->mode?) */ + if (password) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_NONCE, 0)) { + if ((r=nr_stun_receive_response_long_term_auth(ctx->response, ctx))) + ABORT(r); + } + else { + if ((r=nr_stun_receive_response_short_term_auth(ctx->response))) + ABORT(r); + } + } + + if (NR_STUN_GET_TYPE_CLASS(ctx->response->header.type) == NR_CLASS_RESPONSE) { + if ((r=nr_stun_process_success_response(ctx->response))) + ABORT(r); + } + else { + if (fail_on_error) { + ctx->state = NR_STUN_CLIENT_STATE_FAILED; + } + /* Note: most times we call process_error_response, we get r != 0. + + However, if the error is to be discarded, we get r == 0, smash + the error code, and just keep going. + */ + if ((r=nr_stun_process_error_response(ctx->response, &ctx->error_code))) { + ABORT(r); + } + else { + ctx->error_code = 0xffff; + /* drop the error on the floor */ + ABORT(R_FAILED); + } + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Successfully parsed mode=%d",ctx->label,ctx->mode); + +/* TODO: !nn! this should be moved to individual message receive/processing sections */ + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) + ABORT(R_BAD_DATA); + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + ABORT(R_BAD_DATA); + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) { + /* Compensate for a bug in Google's STUN servers where they always respond with MAPPED-ADDRESS */ + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS but MAPPED-ADDRESS. Falling back (though server is wrong).", ctx->label); + } + else { + ABORT(R_BAD_DATA); + } + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0) && ! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) + ABORT(R_BAD_DATA); + + mapped_addr = &ctx->results.stun_binding_response_stund_0_96.mapped_addr; + break; + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) + ABORT(R_BAD_DATA); + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + ABORT(R_BAD_DATA); + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) + ABORT(R_BAD_DATA); + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + ABORT(R_BAD_DATA); + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) + ABORT(R_BAD_DATA); + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + ABORT(R_BAD_DATA); + + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_RELAY_ADDRESS, &attr)) + ABORT(R_BAD_DATA); + + if ((r=nr_stun_transport_addr_check(&attr->u.relay_address.unmasked, + ctx->mapped_addr_check_mask))) + ABORT(r); + + if ((r=nr_transport_addr_copy( + &ctx->results.allocate_response.relay_addr, + &attr->u.relay_address.unmasked))) + ABORT(r); + + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) + ABORT(R_BAD_DATA); + ctx->results.allocate_response.lifetime_secs=attr->u.lifetime_secs; + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received relay address: %s", ctx->label, ctx->results.allocate_response.relay_addr.as_string); + + mapped_addr = &ctx->results.allocate_response.mapped_addr; + + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + ABORT(R_BAD_DATA); + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) + ABORT(R_BAD_DATA); + ctx->results.refresh_response.lifetime_secs=attr->u.lifetime_secs; + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + ABORT(R_BAD_DATA); + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + + /* make sure we have the most up-to-date address from this peer */ + if (nr_transport_addr_cmp(&ctx->peer_addr, peer_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Peer moved from %s to %s", ctx->label, ctx->peer_addr.as_string, peer_addr->as_string); + nr_transport_addr_copy(&ctx->peer_addr, peer_addr); + } + + if (mapped_addr) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, &attr)) { + if ((r=nr_stun_transport_addr_check(&attr->u.xor_mapped_address.unmasked, + ctx->mapped_addr_check_mask))) + ABORT(r); + + if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.xor_mapped_address.unmasked))) + ABORT(r); + } + else if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, &attr)) { + if ((r=nr_stun_transport_addr_check(&attr->u.mapped_address, + ctx->mapped_addr_check_mask))) + ABORT(r); + + if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.mapped_address))) + ABORT(r); + } + else + ABORT(R_BAD_DATA); + + // STUN doesn't distinguish protocol in mapped address, therefore + // assign used protocol from peer_addr + if (mapped_addr->protocol!=peer_addr->protocol){ + mapped_addr->protocol=peer_addr->protocol; + nr_transport_addr_fmt_addr_string(mapped_addr); + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received mapped address: %s", ctx->label, mapped_addr->as_string); + } + + ctx->state=NR_STUN_CLIENT_STATE_DONE; + + _status=0; + abort: + if(_status && response_matched){ + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): Error processing response: %s, stun error code %d.", ctx->label, nr_strerror(_status), (int)ctx->error_code); + } + + if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) && + (ctx->state != NR_STUN_CLIENT_STATE_WAITING)) { + /* Cancel the timer firing */ + if (ctx->timer_handle) { + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle = 0; + } + + nr_stun_client_fire_finished_cb(ctx); + } + + return(_status); + } + +int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + *ctxp=0; + + nr_stun_client_reset(ctx); + + RFREE(ctx->nonce); + RFREE(ctx->realm); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + + +int nr_stun_client_cancel(nr_stun_client_ctx *ctx) + { + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + /* Mark cancelled so we ignore any returned messsages */ + ctx->state=NR_STUN_CLIENT_STATE_CANCELLED; + return(0); +} + +int nr_stun_client_wait(nr_stun_client_ctx *ctx) + { + nr_stun_client_cancel(ctx); + ctx->state=NR_STUN_CLIENT_STATE_WAITING; + + ctx->request_ct = ctx->maximum_transmits; + ctx->timeout_ms = ctx->maximum_transmits_timeout_ms; + NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle); + + return(0); + } + +int nr_stun_client_failed(nr_stun_client_ctx *ctx) + { + nr_stun_client_cancel(ctx); + ctx->state=NR_STUN_CLIENT_STATE_FAILED; + nr_stun_client_fire_finished_cb(ctx); + return(0); + } diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h new file mode 100644 index 000000000..179f16cba --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h @@ -0,0 +1,199 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_client_ctx_h +#define _stun_client_ctx_h + +/* forward declaration */ +typedef struct nr_stun_client_ctx_ nr_stun_client_ctx; + +#include "stun.h" + +/* Checklist for adding new STUN transaction types + + 1. Add new method type in stun.h (NR_METHOD_*) + 2. Add new MSGs in stun.h (NR_STUN_MSG_*) + 3. Add new messages to stun_util.c:nr_stun_msg_type + 4. Add new request type to stun_build.h + 4. Add new message builder to stun_build.c + 5. Add new response type to stun_client_ctx.h + 6. Add new arm to stun_client_ctx.c:nr_stun_client_send_request + 7. Add new arms to nr_stun_client_process_response + 8. Add new arms to stun_hint.c:nr_is_stun_message +*/ + + + + +typedef union nr_stun_client_params_ { + + nr_stun_client_stun_binding_request_params stun_binding_request; + nr_stun_client_stun_keepalive_params stun_keepalive; +#ifdef USE_STUND_0_96 + nr_stun_client_stun_binding_request_stund_0_96_params stun_binding_request_stund_0_96; +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE + nr_stun_client_ice_binding_request_params ice_binding_request; +#endif /* USE_ICE */ + +#ifdef USE_TURN + nr_stun_client_allocate_request_params allocate_request; + nr_stun_client_refresh_request_params refresh_request; + nr_stun_client_permission_request_params permission_request; + nr_stun_client_send_indication_params send_indication; +#endif /* USE_TURN */ + +} nr_stun_client_params; + +typedef struct nr_stun_client_stun_binding_response_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_stun_binding_response_results; + +typedef struct nr_stun_client_stun_binding_response_stund_0_96_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_stun_binding_response_stund_0_96_results; + +#ifdef USE_ICE +typedef struct nr_stun_client_ice_use_candidate_results_ { +#ifdef WIN32 // silly VC++ gives error if no members + int dummy; +#endif +} nr_stun_client_ice_use_candidate_results; + +typedef struct nr_stun_client_ice_binding_response_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_ice_binding_response_results; +#endif /* USE_ICE */ + +#ifdef USE_TURN +typedef struct nr_stun_client_allocate_response_results_ { + nr_transport_addr relay_addr; + nr_transport_addr mapped_addr; + UINT4 lifetime_secs; +} nr_stun_client_allocate_response_results; + +typedef struct nr_stun_client_refresh_response_results_ { + UINT4 lifetime_secs; +} nr_stun_client_refresh_response_results; + +typedef struct nr_stun_client_permission_response_results_ { + UINT4 lifetime_secs; +} nr_stun_client_permission_response_results; + +#endif /* USE_TURN */ + +typedef union nr_stun_client_results_ { + nr_stun_client_stun_binding_response_results stun_binding_response; + nr_stun_client_stun_binding_response_stund_0_96_results stun_binding_response_stund_0_96; + +#ifdef USE_ICE + nr_stun_client_ice_use_candidate_results ice_use_candidate; + nr_stun_client_ice_binding_response_results ice_binding_response; +#endif /* USE_ICE */ + +#ifdef USE_TURN + nr_stun_client_allocate_response_results allocate_response; + nr_stun_client_refresh_response_results refresh_response; +#endif /* USE_TURN */ +} nr_stun_client_results; + +struct nr_stun_client_ctx_ { + int state; +#define NR_STUN_CLIENT_STATE_INITTED 0 +#define NR_STUN_CLIENT_STATE_RUNNING 1 +#define NR_STUN_CLIENT_STATE_DONE 2 +#define NR_STUN_CLIENT_STATE_FAILED 3 +#define NR_STUN_CLIENT_STATE_TIMED_OUT 4 +#define NR_STUN_CLIENT_STATE_CANCELLED 5 +#define NR_STUN_CLIENT_STATE_WAITING 6 + + int mode; +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH 1 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH 2 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH 3 +#define NR_STUN_CLIENT_MODE_KEEPALIVE 4 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96 5 +#ifdef USE_ICE +#define NR_ICE_CLIENT_MODE_USE_CANDIDATE 10 +#define NR_ICE_CLIENT_MODE_BINDING_REQUEST 11 +#endif /* USE_ICE */ +#ifdef USE_TURN +#define NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST 20 +#define NR_TURN_CLIENT_MODE_REFRESH_REQUEST 21 +#define NR_TURN_CLIENT_MODE_SEND_INDICATION 22 +#define NR_TURN_CLIENT_MODE_DATA_INDICATION 24 +#define NR_TURN_CLIENT_MODE_PERMISSION_REQUEST 25 +#endif /* USE_TURN */ + + char *label; + nr_transport_addr my_addr; + nr_transport_addr peer_addr; + nr_socket *sock; + nr_stun_client_auth_params auth_params; + nr_stun_client_params params; + nr_stun_client_results results; + char *nonce; + char *realm; + void *timer_handle; + int request_ct; + UINT4 rto_ms; /* retransmission time out */ + double retransmission_backoff_factor; + UINT4 maximum_transmits; + UINT4 maximum_transmits_timeout_ms; + UINT4 mapped_addr_check_mask; /* What checks to run on mapped addresses */ + int timeout_ms; + struct timeval timer_set; + int retry_ct; + NR_async_cb finished_cb; + void *cb_arg; + nr_stun_message *request; + nr_stun_message *response; + UINT2 error_code; +}; + +int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp); +int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg); +int nr_stun_client_restart(nr_stun_client_ctx *ctx); +int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx); +int nr_stun_client_reset(nr_stun_client_ctx *ctx); +int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp); +int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 mask); +int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr); +int nr_stun_client_cancel(nr_stun_client_ctx *ctx); +int nr_stun_client_wait(nr_stun_client_ctx *ctx); +int nr_stun_client_failed(nr_stun_client_ctx *ctx); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_codec.c b/media/mtransport/third_party/nICEr/src/stun/stun_codec.c new file mode 100644 index 000000000..6faab70de --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_codec.c @@ -0,0 +1,1522 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: stun_codec.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> +#include <stddef.h> + +#include "nr_api.h" +#include "stun.h" +#include "byteorder.h" +#include "r_crc32.h" +#include "nr_crypto.h" +#include "mbslen.h" + +#define NR_STUN_IPV4_FAMILY 0x01 +#define NR_STUN_IPV6_FAMILY 0x02 + +#define SKIP_ATTRIBUTE_DECODE -1 + +static int nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info); + +static int nr_stun_fix_attribute_ordering(nr_stun_message *msg); + +static int nr_stun_encode_htons(UINT2 data, int buflen, UCHAR *buf, int *offset); +static int nr_stun_encode_htonl(UINT4 data, int buflen, UCHAR *buf, int *offset); +static int nr_stun_encode_htonll(UINT8 data, int buflen, UCHAR *buf, int *offset); +static int nr_stun_encode(UCHAR *data, int length, int buflen, UCHAR *buf, int *offset); + +static int nr_stun_decode_htons(UCHAR *buf, int buflen, int *offset, UINT2 *data); +static int nr_stun_decode_htonl(UCHAR *buf, int buflen, int *offset, UINT4 *data); +static int nr_stun_decode_htonll(UCHAR *buf, int buflen, int *offset, UINT8 *data); +static int nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data); + +static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, int len, void *data, int max_bytes, int max_chars); + +static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); +static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); +static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); +static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); +static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data); + + +int +nr_stun_encode_htons(UINT2 data, int buflen, UCHAR *buf, int *offset) +{ + UINT2 d = htons(data); + + if (*offset + sizeof(d) >= buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd >= %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonl(UINT4 data, int buflen, UCHAR *buf, int *offset) +{ + UINT4 d = htonl(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonll(UINT8 data, int buflen, UCHAR *buf, int *offset) +{ + UINT8 d = nr_htonll(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode(UCHAR *data, int length, int buflen, UCHAR *buf, int *offset) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], data, length); + *offset += length; + + return 0; +} + + +int +nr_stun_decode_htons(UCHAR *buf, int buflen, int *offset, UINT2 *data) +{ + UINT2 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htons(d); + + return 0; +} + +int +nr_stun_decode_htonl(UCHAR *buf, int buflen, int *offset, UINT4 *data) +{ + UINT4 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htonl(d); + + return 0; +} + +int +nr_stun_decode_htonll(UCHAR *buf, int buflen, int *offset, UINT8 *data) +{ + UINT8 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = nr_htonll(d); + + return 0; +} + +int +nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(data, &buf[*offset], length); + *offset += length; + + return 0; +} + +int +nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, int len, void *data, int max_bytes, int max_chars) +{ + int _status; + char *s = data; + size_t nchars; + + if (len > max_bytes) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %d bytes", attr_info->name, len); + ABORT(R_FAILED); + } + + if (max_chars >= 0) { + if (mbslen(s, &nchars)) { + /* who knows what to do, just assume everything is working ok */ + } + else if (nchars > max_chars) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %zd characters", attr_info->name, nchars); + ABORT(R_FAILED); + } + } + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data) +{ + int r,_status; + nr_stun_attr_error_code *ec = data; + + if (ec->number < 300 || ec->number > 699) + ABORT(R_FAILED); + + if ((r=nr_stun_attr_string_illegal(attr_info, strlen(ec->reason), ec->reason, NR_STUN_MAX_ERROR_CODE_REASON_BYTES, NR_STUN_MAX_ERROR_CODE_REASON_CHARS))) + ABORT(r); + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_NONCE_BYTES, NR_STUN_MAX_NONCE_CHARS); +} + +int +nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_REALM_BYTES, NR_STUN_MAX_REALM_CHARS); +} + +int +nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_SERVER_BYTES, NR_STUN_MAX_SERVER_CHARS); +} + +int +nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_USERNAME_BYTES, -1); +} + +static int +nr_stun_attr_codec_UCHAR_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UCHAR*)data); + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int start = offset; + UINT4 tmp = *((UCHAR *)data); + tmp <<= 24; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(tmp , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + UINT4 tmp; + + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, &tmp)) + return R_FAILED; + + *((UCHAR *)data) = (tmp >> 24) & 0xff; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UCHAR = { + "UCHAR", + nr_stun_attr_codec_UCHAR_print, + nr_stun_attr_codec_UCHAR_encode, + nr_stun_attr_codec_UCHAR_decode +}; + +static int +nr_stun_attr_codec_UINT4_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UINT4*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT4_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(*(UINT4*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT4_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, (UINT4*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT4 = { + "UINT4", + nr_stun_attr_codec_UINT4_print, + nr_stun_attr_codec_UINT4_encode, + nr_stun_attr_codec_UINT4_decode +}; + +static int +nr_stun_attr_codec_UINT8_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %llu", msg, attr_info->name, *(UINT8*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT8_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT8) , buflen, buf, &offset) + || nr_stun_encode_htonll(*(UINT8*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT8_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + if (attrlen != sizeof(UINT8)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonll(buf, buflen, &offset, (UINT8*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT8 = { + "UINT8", + nr_stun_attr_codec_UINT8_print, + nr_stun_attr_codec_UINT8_encode, + nr_stun_attr_codec_UINT8_decode +}; + +static int +nr_stun_attr_codec_addr_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", msg, attr_info->name, ((nr_transport_addr*)data)->as_string); + return 0; +} + +static int +nr_stun_attr_codec_addr_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int r,_status; + int start = offset; + nr_transport_addr *addr = data; + UCHAR pad = '\0'; + UCHAR family; + + if ((r=nr_stun_encode_htons(attr_info->type, buflen, buf, &offset))) + ABORT(r); + + switch (addr->ip_version) { + case NR_IPV4: + family = NR_STUN_IPV4_FAMILY; + if (nr_stun_encode_htons(8 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr4.sin_port), buflen, buf, &offset) + || nr_stun_encode_htonl(ntohl(addr->u.addr4.sin_addr.s_addr), buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + case NR_IPV6: + family = NR_STUN_IPV6_FAMILY; + if (nr_stun_encode_htons(20 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr6.sin6_port), buflen, buf, &offset) + || nr_stun_encode(addr->u.addr6.sin6_addr.s6_addr, 16, buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_addr_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int _status; + UCHAR pad; + UCHAR family; + UINT2 port; + UINT4 addr4; + struct in6_addr addr6; + nr_transport_addr *result = data; + + if (nr_stun_decode(1, buf, buflen, &offset, &pad) + || nr_stun_decode(1, buf, buflen, &offset, &family)) + ABORT(R_FAILED); + + switch (family) { + case NR_STUN_IPV4_FAMILY: + if (attrlen != 8) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode_htonl(buf, buflen, &offset, &addr4)) + ABORT(R_FAILED); + + if (nr_ip4_port_to_transport_addr(addr4, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + case NR_STUN_IPV6_FAMILY: + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode(16, buf, buflen, &offset, addr6.s6_addr)) + ABORT(R_FAILED); + + if (nr_ip6_port_to_transport_addr(&addr6, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + default: + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal address family: %d", family); + ABORT(R_FAILED); + break; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_addr = { + "addr", + nr_stun_attr_codec_addr_print, + nr_stun_attr_codec_addr_encode, + nr_stun_attr_codec_addr_decode +}; + +static int +nr_stun_attr_codec_data_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_data *d = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)d->data, d->length); + return 0; +} + +static int +nr_stun_attr_codec_data_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + nr_stun_attr_data *d = data; + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(d->length , buflen, buf, &offset) + || nr_stun_encode(d->data, d->length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_data_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int _status; + nr_stun_attr_data *result = data; + + /* -1 because it is going to be null terminated just to be safe */ + if (attrlen >= (sizeof(result->data) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too much data: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->data)) + ABORT(R_FAILED); + + result->length = attrlen; + result->data[attrlen] = '\0'; /* just to be nice */ + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_data = { + "data", + nr_stun_attr_codec_data_print, + nr_stun_attr_codec_data_encode, + nr_stun_attr_codec_data_decode +}; + +static int +nr_stun_attr_codec_error_code_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_error_code *error_code = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %d %s", + msg, attr_info->name, error_code->number, + error_code->reason); + return 0; +} + +static int +nr_stun_attr_codec_error_code_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + nr_stun_attr_error_code *error_code = data; + int start = offset; + int length = strlen(error_code->reason); + UCHAR pad[2] = { 0 }; + UCHAR class = error_code->number / 100; + UCHAR number = error_code->number % 100; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(4 + length , buflen, buf, &offset) + || nr_stun_encode(pad, 2 , buflen, buf, &offset) + || nr_stun_encode(&class, 1 , buflen, buf, &offset) + || nr_stun_encode(&number, 1 , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)error_code->reason, length, buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_error_code_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int _status; + nr_stun_attr_error_code *result = data; + UCHAR pad[2]; + UCHAR class; + UCHAR number; + int size_reason; + + if (nr_stun_decode(2, buf, buflen, &offset, pad) + || nr_stun_decode(1, buf, buflen, &offset, &class) + || nr_stun_decode(1, buf, buflen, &offset, &number)) + ABORT(R_FAILED); + + result->number = (class * 100) + number; + + size_reason = attrlen - 4; + + /* -1 because the string will be null terminated */ + if (size_reason > (sizeof(result->reason) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Reason is too large, truncating"); + /* don't fail, but instead truncate the reason */ + size_reason = sizeof(result->reason) - 1; + } + + if (nr_stun_decode(size_reason, buf, buflen, &offset, (UCHAR*)result->reason)) + ABORT(R_FAILED); + result->reason[size_reason] = '\0'; + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_error_code = { + "error_code", + nr_stun_attr_codec_error_code_print, + nr_stun_attr_codec_error_code_encode, + nr_stun_attr_codec_error_code_decode +}; + +static int +nr_stun_attr_codec_fingerprint_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_fingerprint *fingerprint = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %08x", msg, attr_info->name, fingerprint->checksum); + return 0; +} + +static int +nr_stun_attr_codec_fingerprint_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + UINT4 checksum; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + header->length = ntohs(header->length); + header->length += 8; /* Fingerprint */ + header->length = htons(header->length); + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + return R_FAILED; + } + + fingerprint->checksum = checksum ^ 0x5354554e; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum); + + fingerprint->valid = 1; + return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int r,_status; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + int length; + UINT4 checksum; + + if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum))) + ABORT(r); + + offset -= 4; /* rewind to before the length and type fields */ + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + length = offset; /* right before FINGERPRINT */ + length -= sizeof(*header); /* remove header length */ + length += 8; /* add length of Fingerprint */ + header->length = htons(length); + + /* make sure FINGERPRINT is final attribute in message */ + assert(length + sizeof(*header) == buflen); + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + ABORT(R_FAILED); + } + + fingerprint->valid = (fingerprint->checksum == (checksum ^ 0x5354554e)); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", (checksum ^ 0x5354554e)); + if (! fingerprint->valid) + r_log(NR_LOG_STUN, LOG_WARNING, "Invalid FINGERPRINT %08x", fingerprint->checksum); + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_fingerprint = { + "fingerprint", + nr_stun_attr_codec_fingerprint_print, + nr_stun_attr_codec_fingerprint_encode, + nr_stun_attr_codec_fingerprint_decode +}; + +static int +nr_stun_attr_codec_flag_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: on", msg, attr_info->name); + return 0; +} + +static int +nr_stun_attr_codec_flag_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(0 , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_flag_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + if (attrlen != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal flag length: %d", attrlen); + return R_FAILED; + } + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_flag = { + "flag", + nr_stun_attr_codec_flag_print, + nr_stun_attr_codec_flag_encode, + nr_stun_attr_codec_flag_decode +}; + +static int +nr_stun_attr_codec_message_integrity_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_message_integrity *integrity = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)integrity->hash, sizeof(integrity->hash)); + return 0; +} + +static int +nr_stun_compute_message_integrity(UCHAR *buf, int offset, UCHAR *password, int passwordlen, UCHAR *computedHMAC) +{ + int r,_status; + UINT2 hold; + UINT2 length; + nr_stun_message_header *header; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computing MESSAGE-INTEGRITY"); + + header = (nr_stun_message_header*)buf; + hold = header->length; + + /* adjust the length of the message */ + length = offset; + length -= sizeof(*header); + length += 24; /* for MESSAGE-INTEGRITY attribute */ + header->length = htons(length); + + if ((r=nr_crypto_hmac_sha1((UCHAR*)password, passwordlen, + buf, offset, computedHMAC))) + ABORT(r); + + r_dump(NR_LOG_STUN, LOG_DEBUG, "Computed MESSAGE-INTEGRITY ", (char*)computedHMAC, 20); + + _status=0; + abort: + header->length = hold; + return _status; +} + +static int +nr_stun_attr_codec_message_integrity_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int start = offset; + nr_stun_attr_message_integrity *integrity = data; + + if (nr_stun_compute_message_integrity(buf, offset, integrity->password, integrity->passwordlen, integrity->hash)) + return R_FAILED; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(integrity->hash) , buflen, buf, &offset) + || nr_stun_encode(integrity->hash, sizeof(integrity->hash) , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_message_integrity_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int _status; + int start; + nr_stun_attr_message_integrity *result = data; + UCHAR computedHMAC[20]; + + result->valid = 0; + + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s must be 20 bytes, not %d", attr_info->name, attrlen); + ABORT(R_FAILED); + } + + start = offset - 4; /* rewind to before the length and type fields */ + if (start < 0) + ABORT(R_INTERNAL); + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->hash)) + ABORT(R_FAILED); + + if (result->unknown_user) { + result->valid = 0; + } + else { + if (nr_stun_compute_message_integrity(buf, start, result->password, result->passwordlen, computedHMAC)) + ABORT(R_FAILED); + + assert(sizeof(computedHMAC) == sizeof(result->hash)); + + result->valid = (memcmp(computedHMAC, result->hash, 20) == 0); + } + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_message_integrity = { + "message_integrity", + nr_stun_attr_codec_message_integrity_print, + nr_stun_attr_codec_message_integrity_encode, + nr_stun_attr_codec_message_integrity_decode +}; + +static int +nr_stun_attr_codec_noop_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + return SKIP_ATTRIBUTE_DECODE; +} + +nr_stun_attr_codec nr_stun_attr_codec_noop = { + "NOOP", + 0, /* ignore, never print these attributes */ + 0, /* ignore, never encode these attributes */ + nr_stun_attr_codec_noop_decode +}; + +static int +nr_stun_attr_codec_quoted_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_quoted_string_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ +//TODO: !nn! syntax check, conversion if not quoted already? +//We'll just restrict this in the API -- EKR + return nr_stun_attr_codec_string.encode(attr_info, data, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_quoted_string_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ +//TODO: !nn! I don't see any need to unquote this but we may +//find one later -- EKR + return nr_stun_attr_codec_string.decode(attr_info, attrlen, buf, offset, buflen, data); +} + +nr_stun_attr_codec nr_stun_attr_codec_quoted_string = { + "quoted_string", + nr_stun_attr_codec_quoted_string_print, + nr_stun_attr_codec_quoted_string_encode, + nr_stun_attr_codec_quoted_string_decode +}; + +static int +nr_stun_attr_codec_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_string_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int start = offset; + char *str = data; + int length = strlen(str); + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)str, length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_string_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int _status; + char *result = data; + + /* actual enforcement of the specific string size happens elsewhere */ + if (attrlen >= NR_STUN_MAX_STRING_SIZE) { + r_log(NR_LOG_STUN, LOG_WARNING, "String is too large: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, (UCHAR*)result)) + ABORT(R_FAILED); + result[attrlen] = '\0'; /* just to be nice */ + + if (strlen(result) != attrlen) { + /* stund 0.96 sends a final null in the Server attribute, so + * only error if the null appears anywhere else in a string */ + if (strlen(result) != attrlen-1) { + r_log(NR_LOG_STUN, LOG_WARNING, "Error in string: %zd/%d", strlen(result), attrlen); + ABORT(R_FAILED); + } + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_string = { + "string", + nr_stun_attr_codec_string_print, + nr_stun_attr_codec_string_encode, + nr_stun_attr_codec_string_decode +}; + +static int +nr_stun_attr_codec_unknown_attributes_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_unknown_attributes *unknown_attributes = data; + char type[9]; + char str[64 + (NR_STUN_MAX_UNKNOWN_ATTRIBUTES * sizeof(type))]; + int i; + + snprintf(str, sizeof(str), "%s %s:", msg, attr_info->name); + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + snprintf(type, sizeof(type), "%s 0x%04x", ((i>0)?",":""), unknown_attributes->attribute[i]); + strlcat(str, type, sizeof(str)); + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "%s", str); + return 0; +} + +static int +nr_stun_attr_codec_unknown_attributes_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + int _status; + int start = offset; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int length = (2 * unknown_attributes->num_attributes); + int i; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_FAILED); + } + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset)) + ABORT(R_FAILED); + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + if (nr_stun_encode_htons(unknown_attributes->attribute[i], buflen, buf, &offset)) + ABORT(R_FAILED); + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_unknown_attributes_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int _status; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int i; + UINT2 *a; + + if ((attrlen % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute is illegal size: %d", attrlen); + ABORT(R_REJECTED); + } + + unknown_attributes->num_attributes = attrlen / 2; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_REJECTED); + } + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + a = &(unknown_attributes->attribute[i]); + if (nr_stun_decode_htons(buf, buflen, &offset, a)) + return R_FAILED; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes = { + "unknown_attributes", + nr_stun_attr_codec_unknown_attributes_print, + nr_stun_attr_codec_unknown_attributes_encode, + nr_stun_attr_codec_unknown_attributes_decode +}; + +static int +nr_stun_attr_codec_xor_mapped_address_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s (unmasked) %s (masked)", + msg, attr_info->name, + xor_mapped_address->unmasked.as_string, + xor_mapped_address->masked.as_string); + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_encode(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->unmasked, &xor_mapped_address->masked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + if (nr_stun_attr_codec_addr.encode(attr_info, &xor_mapped_address->masked, offset, buflen, buf, attrlen)) + return R_FAILED; + + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) +{ + int r,_status; + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + if ((r=nr_stun_attr_codec_addr.decode(attr_info, attrlen, buf, offset, buflen, &xor_mapped_address->masked))) + ABORT(r); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->masked, &xor_mapped_address->unmasked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + 0, /* never encode this type */ + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address = { + "xor_peer_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +#define NR_ADD_STUN_ATTRIBUTE(type, name, codec, illegal) \ + { (type), (name), &(codec), illegal }, + +#define NR_ADD_STUN_ATTRIBUTE_IGNORE(type, name) \ + { (type), (name), &nr_stun_attr_codec_noop, 0 }, + + +static nr_stun_attr_info attrs[] = { + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ALTERNATE_SERVER, "ALTERNATE-SERVER", nr_stun_attr_codec_addr, 0) +#ifdef USE_STUND_0_96 + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_CHANGE_REQUEST, "CHANGE-REQUEST", nr_stun_attr_codec_UINT4, 0) +#endif + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ERROR_CODE, "ERROR-CODE", nr_stun_attr_codec_error_code, nr_stun_attr_error_code_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_FINGERPRINT, "FINGERPRINT", nr_stun_attr_codec_fingerprint, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MAPPED_ADDRESS, "MAPPED-ADDRESS", nr_stun_attr_codec_addr, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MESSAGE_INTEGRITY, "MESSAGE-INTEGRITY", nr_stun_attr_codec_message_integrity, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_NONCE, "NONCE", nr_stun_attr_codec_quoted_string, nr_stun_attr_nonce_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REALM, "REALM", nr_stun_attr_codec_quoted_string, nr_stun_attr_realm_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_SERVER, "SERVER", nr_stun_attr_codec_string, nr_stun_attr_server_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, "UNKNOWN-ATTRIBUTES", nr_stun_attr_codec_unknown_attributes, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USERNAME, "USERNAME", nr_stun_attr_codec_string, nr_stun_attr_username_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_MAPPED_ADDRESS, "XOR-MAPPED-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + +#ifdef USE_ICE + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLED, "ICE-CONTROLLED", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLING, "ICE-CONTROLLING", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_PRIORITY, "PRIORITY", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USE_CANDIDATE, "USE-CANDIDATE", nr_stun_attr_codec_flag, 0) +#endif + +#ifdef USE_TURN + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_DATA, "DATA", nr_stun_attr_codec_data, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_LIFETIME, "LIFETIME", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_RELAY_ADDRESS, "XOR-RELAY-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_PEER_ADDRESS, "XOR-PEER-ADDRESS", nr_stun_attr_codec_xor_peer_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REQUESTED_TRANSPORT, "REQUESTED-TRANSPORT", nr_stun_attr_codec_UCHAR, 0) +#endif /* USE_TURN */ + + /* for backwards compatibilty */ + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS, "Old XOR-MAPPED-ADDRESS", nr_stun_attr_codec_old_xor_mapped_address, 0) +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_RESPONSE_ADDRESS, "RESPONSE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_SOURCE_ADDRESS, "SOURCE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_CHANGED_ADDRESS, "CHANGED-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_PASSWORD, "PASSWORD") +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ +}; + + +int +nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info) +{ + int _status; + int i; + + *info = 0; + for (i = 0; i < sizeof(attrs)/sizeof(*attrs); ++i) { + if (type == attrs[i].type) { + *info = &attrs[i]; + break; + } + } + + if (*info == 0) + ABORT(R_NOT_FOUND); + + _status=0; + abort: + return(_status); +} + +int +nr_stun_fix_attribute_ordering(nr_stun_message *msg) +{ + nr_stun_message_attribute *message_integrity; + nr_stun_message_attribute *fingerprint; + + /* 2nd to the last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &message_integrity)) { + TAILQ_REMOVE(&msg->attributes, message_integrity, entry); + TAILQ_INSERT_TAIL(&msg->attributes, message_integrity, entry); + } + + /* last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &fingerprint)) { + TAILQ_REMOVE(&msg->attributes, fingerprint, entry); + TAILQ_INSERT_TAIL(&msg->attributes, fingerprint, entry); + } + + return 0; +} + +#ifdef SANITY_CHECKS +static void sanity_check_encoding_stuff(nr_stun_message *msg) +{ + nr_stun_message_attribute *attr = 0; + int padding_bytes; + int l; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Starting to sanity check encoding"); + + l = 0; + TAILQ_FOREACH(attr, &msg->attributes, entry) { + padding_bytes = 0; + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + } + assert(attr->length == (attr->encoding_length - (4 + padding_bytes))); + assert(((void*)attr->encoding) == (msg->buffer + 20 + l)); + l += attr->encoding_length; + assert((l % 4) == 0); + } + assert(l == msg->header.length); +} +#endif /* SANITY_CHECKS */ + + +int +nr_stun_encode_message(nr_stun_message *msg) +{ + int r,_status; + int length_offset; + int length_offset_hold; + nr_stun_attr_info *attr_info; + nr_stun_message_attribute *attr; + int padding_bytes; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoding STUN message"); + + nr_stun_fix_attribute_ordering(msg); + + msg->name = nr_stun_msg_type(msg->header.type); + msg->length = 0; + msg->header.length = 0; + + if ((r=nr_stun_encode_htons(msg->header.type, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: 0x%03x", msg->header.type); + + /* grab the offset to be used later to re-write the header length field */ + length_offset_hold = msg->length; + + if ((r=nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + + if ((r=nr_stun_encode_htonl(msg->header.magic_cookie, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Cookie: %08x", msg->header.magic_cookie); + + if ((r=nr_stun_encode((UCHAR*)(&msg->header.id), sizeof(msg->header.id), sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Encoded ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + TAILQ_FOREACH(attr, &msg->attributes, entry) { + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unrecognized attribute: 0x%04x", attr->type); + ABORT(R_INTERNAL); + } + + attr->name = attr_info->name; + attr->type_name = attr_info->codec->name; + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[msg->length]; + + if (attr_info->codec->encode != 0) { + if ((r=attr_info->codec->encode(attr_info, &attr->u, msg->length, sizeof(msg->buffer), msg->buffer, &attr->encoding_length))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to encode %s", attr_info->name); + ABORT(r); + } + + msg->length += attr->encoding_length; + attr->length = attr->encoding_length - 4; /* -4 for type and length fields */ + + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) + ABORT(r); + } + + attr_info->codec->print(attr_info, "Encoded", &attr->u); + + if ((attr->length % 4) == 0) { + padding_bytes = 0; + } + else { + padding_bytes = 4 - (attr->length % 4); + nr_stun_encode((UCHAR*)"\0\0\0\0", padding_bytes, sizeof(msg->buffer), msg->buffer, &msg->length); + attr->encoding_length += padding_bytes; + } + + msg->header.length += attr->encoding_length; + length_offset = length_offset_hold; + (void)nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &length_offset); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing encode function for attribute: %s", attr_info->name); + } + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Length: %d", msg->header.length); + + assert(msg->length < NR_STUN_MAX_MESSAGE_SIZE); + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; +abort: + return _status; +} + +int +nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg) +{ + int r,_status; + int offset; + int size; + int padding_bytes; + nr_stun_message_attribute *attr; + nr_stun_attr_info *attr_info; + Data *password; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsing STUN message of %d bytes", msg->length); + + if (!TAILQ_EMPTY(&msg->attributes)) + ABORT(R_BAD_ARGS); + + if (sizeof(nr_stun_message_header) > msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Message too small"); + ABORT(R_FAILED); + } + + memcpy(&msg->header, msg->buffer, sizeof(msg->header)); + msg->header.type = ntohs(msg->header.type); + msg->header.length = ntohs(msg->header.length); + msg->header.magic_cookie = ntohl(msg->header.magic_cookie); + + msg->name = nr_stun_msg_type(msg->header.type); + + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: 0x%03x", msg->header.type); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Length: %d", msg->header.length); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Cookie: %08x", msg->header.magic_cookie); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Parsed ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + if (msg->header.length + sizeof(msg->header) != msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Inconsistent message header length: %d/%d", + msg->header.length, msg->length); + ABORT(R_FAILED); + } + + size = msg->header.length; + + if ((size % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message size: %d", msg->header.length); + ABORT(R_FAILED); + } + + offset = sizeof(msg->header); + + while (size > 0) { + r_log(NR_LOG_STUN, LOG_DEBUG, "size = %d", size); + + if (size < 4) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message length: %d", size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_message_attribute_create(msg, &attr))) + ABORT(R_NO_MEMORY); + + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[offset]; + attr->type = ntohs(attr->encoding->type); + attr->length = ntohs(attr->encoding->length); + attr->encoding_length = attr->length + 4; + + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + attr->encoding_length += padding_bytes; + } + + if ((attr->encoding_length) > size) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute length larger than remaining message size: %d/%d", attr->encoding_length, size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + if (attr->type <= 0x7FFF) + ++msg->comprehension_required_unknown_attributes; + else + ++msg->comprehension_optional_unknown_attributes; + r_log(NR_LOG_STUN, LOG_INFO, "Unrecognized attribute: 0x%04x", attr->type); + } + else { + attr_info->name = attr_info->name; + attr->type_name = attr_info->codec->name; + + if (attr->type == NR_STUN_ATTR_MESSAGE_INTEGRITY) { + if (get_password && get_password(arg, msg, &password) == 0) { + if (password->len > sizeof(attr->u.message_integrity.password)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Password too long: %d bytes", password->len); + ABORT(R_FAILED); + } + + memcpy(attr->u.message_integrity.password, password->data, password->len); + attr->u.message_integrity.passwordlen = password->len; + } + else { + /* set to user "not found" */ + attr->u.message_integrity.unknown_user = 1; + } + } + else if (attr->type == NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS) { + attr->type = NR_STUN_ATTR_XOR_MAPPED_ADDRESS; + r_log(NR_LOG_STUN, LOG_INFO, "Translating obsolete XOR-MAPPED-ADDRESS type"); + } + + if ((r=attr_info->codec->decode(attr_info, attr->length, msg->buffer, offset+4, msg->length, &attr->u))) { + if (r == SKIP_ATTRIBUTE_DECODE) { + r_log(NR_LOG_STUN, LOG_INFO, "Skipping %s", attr_info->name); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to parse %s", attr_info->name); + } + + attr->invalid = 1; + } + else { + attr_info->codec->print(attr_info, "Parsed", &attr->u); + +#ifdef USE_STUN_PEDANTIC + r_log(NR_LOG_STUN, LOG_DEBUG, "Before pedantic attr_info checks"); + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Failed pedantic attr_info checks"); + ABORT(r); + } + } + r_log(NR_LOG_STUN, LOG_DEBUG, "After pedantic attr_info checks"); +#endif /* USE_STUN_PEDANTIC */ + } + } + + offset += attr->encoding_length; + size -= attr->encoding_length; + } + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; + abort: + return _status; +} + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_codec.h b/media/mtransport/third_party/nICEr/src/stun/stun_codec.h new file mode 100644 index 000000000..53e64334c --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_codec.h @@ -0,0 +1,78 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_codec_h +#define _stun_codec_h + +#include "stun_msg.h" + +typedef struct nr_stun_attr_info_ nr_stun_attr_info; + +typedef struct nr_stun_attr_codec_ { + char *name; + int (*print)(nr_stun_attr_info *attr_info, char *msg, void *data); + int (*encode)(nr_stun_attr_info *attr_info, void *data, int offset, int buflen, UCHAR *buf, int *attrlen); + int (*decode)(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data); +} nr_stun_attr_codec; + +struct nr_stun_attr_info_ { + UINT2 type; + char *name; + nr_stun_attr_codec *codec; + int (*illegal)(nr_stun_attr_info *attr_info, int attrlen, void *data); +}; + +extern nr_stun_attr_codec nr_stun_attr_codec_UINT4; +extern nr_stun_attr_codec nr_stun_attr_codec_UINT8; +extern nr_stun_attr_codec nr_stun_attr_codec_addr; +extern nr_stun_attr_codec nr_stun_attr_codec_bytes; +extern nr_stun_attr_codec nr_stun_attr_codec_data; +extern nr_stun_attr_codec nr_stun_attr_codec_error_code; +extern nr_stun_attr_codec nr_stun_attr_codec_fingerprint; +extern nr_stun_attr_codec nr_stun_attr_codec_flag; +extern nr_stun_attr_codec nr_stun_attr_codec_message_integrity; +extern nr_stun_attr_codec nr_stun_attr_codec_noop; +extern nr_stun_attr_codec nr_stun_attr_codec_quoted_string; +extern nr_stun_attr_codec nr_stun_attr_codec_string; +extern nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes; +extern nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address; +extern nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address; +extern nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address; + + +int nr_stun_encode_message(nr_stun_message *msg); +int nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_hint.c b/media/mtransport/third_party/nICEr/src/stun/stun_hint.c new file mode 100644 index 000000000..941b14d64 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_hint.c @@ -0,0 +1,249 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: stun_hint.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + + +#include <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" + + +/* returns 0 if it's not a STUN message + * 1 if it's likely to be a STUN message + * 2 if it's super likely to be a STUN message + * 3 if it really is a STUN message */ +int +nr_is_stun_message(UCHAR *buf, int len) +{ + const UINT4 cookie = htonl(NR_STUN_MAGIC_COOKIE); + const UINT4 cookie2 = htonl(NR_STUN_MAGIC_COOKIE2); +#if 0 + nr_stun_message msg; +#endif + UINT2 type; + nr_stun_encoded_attribute* attr; + unsigned int attrLen; + int atrType; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if ((buf[0] & (0x80|0x40)) != 0) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + switch (type) { + case NR_STUN_MSG_BINDING_REQUEST: + case NR_STUN_MSG_BINDING_INDICATION: + case NR_STUN_MSG_BINDING_RESPONSE: + case NR_STUN_MSG_BINDING_ERROR_RESPONSE: + +#ifdef USE_TURN + case NR_STUN_MSG_ALLOCATE_REQUEST: + case NR_STUN_MSG_ALLOCATE_RESPONSE: + case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE: + case NR_STUN_MSG_REFRESH_REQUEST: + case NR_STUN_MSG_REFRESH_RESPONSE: + case NR_STUN_MSG_REFRESH_ERROR_RESPONSE: + case NR_STUN_MSG_PERMISSION_REQUEST: + case NR_STUN_MSG_PERMISSION_RESPONSE: + case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE: + case NR_STUN_MSG_CHANNEL_BIND_REQUEST: + case NR_STUN_MSG_CHANNEL_BIND_RESPONSE: + case NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE: + case NR_STUN_MSG_SEND_INDICATION: + case NR_STUN_MSG_DATA_INDICATION: +#ifdef NR_STUN_MSG_CONNECT_REQUEST + case NR_STUN_MSG_CONNECT_REQUEST: +#endif +#ifdef NR_STUN_MSG_CONNECT_RESPONSE + case NR_STUN_MSG_CONNECT_RESPONSE: +#endif +#ifdef NR_STUN_MSG_CONNECT_ERROR_RESPONSE + case NR_STUN_MSG_CONNECT_ERROR_RESPONSE: +#endif +#ifdef NR_STUN_MSG_CONNECT_STATUS_INDICATION + case NR_STUN_MSG_CONNECT_STATUS_INDICATION: +#endif +#endif /* USE_TURN */ + + /* ok so far, continue */ + break; + default: + return 0; + break; + } + + if (!memcmp(&cookie2, &buf[4], sizeof(UINT4))) { + /* return here because if it's an old-style message then there will + * not be a fingerprint in the message */ + return 1; + } + + if (memcmp(&cookie, &buf[4], sizeof(UINT4))) + return 0; + + /* the magic cookie was right, so it's pretty darn likely that what we've + * got here is a STUN message */ + + attr = (nr_stun_encoded_attribute*)(buf + (len - 8)); + attrLen = ntohs(attr->length); + atrType = ntohs(attr->type); + + if (atrType != NR_STUN_ATTR_FINGERPRINT || attrLen != 4) + return 1; + + /* the fingerprint is in the right place and looks sane, so we can be quite + * sure we've got a STUN message */ + +#if 0 +/* nevermind this check ... there's a reasonable chance that a NAT has modified + * the message (and thus the fingerprint check will fail), but it's still an + * otherwise-perfectly-good STUN message, so skip the check since we're going + * to return "true" whether the check succeeds or fails */ + + if (nr_stun_parse_attr_UINT4(buf + (len - 4), attrLen, &msg.fingerprint)) + return 2; + + + if (nr_stun_compute_fingerprint(buf, len - 8, &computedFingerprint)) + return 2; + + if (msg.fingerprint.number != computedFingerprint) + return 2; + + /* and the fingerprint is good, so it's gotta be a STUN message */ +#endif + + return 3; +} + +int +nr_is_stun_request_message(UCHAR *buf, int len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_REQUEST; +} + +int +nr_is_stun_indication_message(UCHAR *buf, int len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_INDICATION; +} + +int +nr_is_stun_response_message(UCHAR *buf, int len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_RESPONSE + || NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_ERROR_RESPONSE; +} + +int +nr_has_stun_cookie(UCHAR *buf, int len) +{ + static UINT4 cookie; + + cookie = htonl(NR_STUN_MAGIC_COOKIE); + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (memcmp(&cookie, &buf[4], sizeof(UINT4))) + return 0; + + return 1; +} + +int +nr_stun_message_length(UCHAR *buf, int buf_len, int *msg_len) +{ + nr_stun_message_header *hdr; + + if (!nr_is_stun_message(buf, buf_len)) + return(R_BAD_DATA); + + hdr = (nr_stun_message_header *)buf; + + *msg_len = ntohs(hdr->length); + + return(0); +} + + + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_hint.h b/media/mtransport/third_party/nICEr/src/stun/stun_hint.h new file mode 100644 index 000000000..4b285d31d --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_hint.h @@ -0,0 +1,44 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _stun_hint_h +#define _stun_hint_h + +int nr_is_stun_message(UCHAR *buf, int len); +int nr_is_stun_request_message(UCHAR *buf, int len); +int nr_is_stun_response_message(UCHAR *buf, int len); +int nr_is_stun_indication_message(UCHAR *buf, int len); +int nr_has_stun_cookie(UCHAR *buf, int len); +int nr_stun_message_length(UCHAR *buf, int len, int *length); + +#endif diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_msg.c b/media/mtransport/third_party/nICEr/src/stun/stun_msg.c new file mode 100644 index 000000000..c34bb46b0 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_msg.c @@ -0,0 +1,360 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: stun_msg.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" + + +int +nr_stun_message_create(nr_stun_message **msg) +{ + int _status; + nr_stun_message *m = 0; + + m = RCALLOC(sizeof(*m)); + if (!m) + ABORT(R_NO_MEMORY); + + TAILQ_INIT(&m->attributes); + + *msg = m; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, int length) +{ + int r,_status; + nr_stun_message *m = 0; + + if (length > sizeof(m->buffer)) { + ABORT(R_BAD_DATA); + } + + if ((r=nr_stun_message_create(&m))) + ABORT(r); + + memcpy(m->buffer, buffer, length); + m->length = length; + + *msg = m; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_destroy(nr_stun_message **msg) +{ + int _status; + nr_stun_message_attribute_head *attrs; + nr_stun_message_attribute *attr; + + if (msg && *msg) { + attrs = &(*msg)->attributes; + while (!TAILQ_EMPTY(attrs)) { + attr = TAILQ_FIRST(attrs); + nr_stun_message_attribute_destroy(*msg, &attr); + } + + RFREE(*msg); + + *msg = 0; + } + + _status=0; +/* abort: */ + return(_status); +} + +int +nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr) +{ + int _status; + nr_stun_message_attribute *a = 0; + + a = RCALLOC(sizeof(*a)); + if (!a) + ABORT(R_NO_MEMORY); + + TAILQ_INSERT_TAIL(&msg->attributes, a, entry); + + *attr = a; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr) +{ + int _status; + nr_stun_message_attribute *a = 0; + + if (attr && *attr) { + a = *attr; + TAILQ_REMOVE(&msg->attributes, a, entry); + + RFREE(a); + + *attr = 0; + } + + _status=0; +/* abort: */ + return(_status); +} + +int +nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute) +{ + nr_stun_message_attribute *attr = 0; + + if (attribute) + *attribute = 0; + + TAILQ_FOREACH(attr, &msg->attributes, entry) { + if (attr->type == type) + break; + } + + if (!attr || attr->invalid) + return 0; /* does not have */ + + if (attribute) + *attribute = attr; + + return 1; /* has */ +} + +#define NR_STUN_MESSAGE_ADD_ATTRIBUTE(__type, __code) \ + { \ + int r,_status; \ + nr_stun_message_attribute *attr = 0; \ + if ((r=nr_stun_message_attribute_create(msg, &attr))) \ + ABORT(r); \ + attr->type = (__type); \ + { __code } \ + _status=0; \ + abort: \ + if (_status){ \ + nr_stun_message_attribute_destroy(msg, &attr); \ + } \ + return(_status); \ + } + + +int +nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ALTERNATE_SERVER, + { + if ((r=nr_transport_addr_copy(&attr->u.alternate_server, alternate_server))) + ABORT(r); + } +) + +int +nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ERROR_CODE, + { + attr->u.error_code.number = number; + strlcpy(attr->u.error_code.reason, reason, sizeof(attr->u.error_code.reason)); + } +) + +int +nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_FINGERPRINT, + {} +) + +int +nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_MESSAGE_INTEGRITY, + { + if (sizeof(attr->u.message_integrity.password) < password->len) + ABORT(R_BAD_DATA); + + memcpy(attr->u.message_integrity.password, password->data, password->len); + attr->u.message_integrity.passwordlen = password->len; + } +) + +int +nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_NONCE, + { strlcpy(attr->u.nonce, nonce, sizeof(attr->u.nonce)); } +) + +int +nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_REALM, + { strlcpy(attr->u.realm, realm, sizeof(attr->u.realm)); } +) + +int +nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_SERVER, + { strlcpy(attr->u.server_name, server_name, sizeof(attr->u.server_name)); } +) + +int +nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, + { memcpy(&attr->u.unknown_attributes, unknown_attributes, sizeof(attr->u.unknown_attributes)); } +) + +int +nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_USERNAME, + { strlcpy(attr->u.username, username, sizeof(attr->u.username)); } +) + +int +nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR protocol) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_REQUESTED_TRANSPORT, + { attr->u.requested_transport = protocol; } +) + +int +nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_XOR_MAPPED_ADDRESS, + { + if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, mapped_address))) + ABORT(r); + } +) + +int +nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_XOR_PEER_ADDRESS, + { + if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, peer_address))) + ABORT(r); + } +) + +#ifdef USE_ICE +int +nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ICE_CONTROLLED, + { attr->u.ice_controlled = ice_controlled; } +) + +int +nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ICE_CONTROLLING, + { attr->u.ice_controlling = ice_controlling; } +) + +int +nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_PRIORITY, + { attr->u.priority = priority; } +) + +int +nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_USE_CANDIDATE, + {} +) +#endif /* USE_ICE */ + +#ifdef USE_TURN +int +nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length) + +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_DATA, + { + if (length > NR_STUN_MAX_MESSAGE_SIZE) + ABORT(R_BAD_ARGS); + + memcpy(attr->u.data.data, data, length); + attr->u.data.length=length; + } +) + +int +nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_LIFETIME, + { attr->u.lifetime_secs = lifetime_secs; } +) + +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 +int +nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_OLD_CHANGE_REQUEST, + { attr->u.change_request = change_request; } +) +#endif /* USE_STUND_0_96 */ + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_msg.h b/media/mtransport/third_party/nICEr/src/stun/stun_msg.h new file mode 100644 index 000000000..927ce2e3c --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_msg.h @@ -0,0 +1,206 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_msg_h +#define _stun_msg_h + +#include "csi_platform.h" +#include "nr_api.h" +#include "transport_addr.h" + +#define NR_STUN_MAX_USERNAME_BYTES 513 +#define NR_STUN_MAX_ERROR_CODE_REASON_BYTES 763 +#define NR_STUN_MAX_ERROR_CODE_REASON_CHARS 128 +#define NR_STUN_MAX_REALM_BYTES 763 +#define NR_STUN_MAX_REALM_CHARS 128 +#define NR_STUN_MAX_NONCE_BYTES 763 +#define NR_STUN_MAX_NONCE_CHARS 128 +#define NR_STUN_MAX_SERVER_BYTES 763 +#define NR_STUN_MAX_SERVER_CHARS 128 +#define NR_STUN_MAX_STRING_SIZE 763 /* any possible string */ +#define NR_STUN_MAX_UNKNOWN_ATTRIBUTES 16 +#define NR_STUN_MAX_MESSAGE_SIZE 2048 + +#define NR_STUN_MAGIC_COOKIE 0x2112A442 +#define NR_STUN_MAGIC_COOKIE2 0xc5cb4e1d /* used recognize old stun messages */ + +typedef struct { UCHAR octet[12]; } UINT12; + +typedef struct nr_stun_attr_error_code_ { + UINT2 number; + char reason[NR_STUN_MAX_ERROR_CODE_REASON_BYTES+1]; /* +1 for \0 */ +} nr_stun_attr_error_code; + +typedef struct nr_stun_attr_fingerprint_ { + UINT4 checksum; + int valid; +} nr_stun_attr_fingerprint; + +typedef struct nr_stun_attr_message_integrity_ { + UCHAR hash[20]; + int unknown_user; + UCHAR password[1024]; + int passwordlen; + int valid; +} nr_stun_attr_message_integrity; + +typedef struct nr_stun_attr_unknown_attributes_ { + UINT2 attribute[NR_STUN_MAX_UNKNOWN_ATTRIBUTES]; + int num_attributes; +} nr_stun_attr_unknown_attributes; + +typedef struct nr_stun_attr_xor_mapped_address_ { + nr_transport_addr masked; + nr_transport_addr unmasked; +} nr_stun_attr_xor_mapped_address; + +typedef struct nr_stun_attr_data_ { + UCHAR data[NR_STUN_MAX_MESSAGE_SIZE]; + int length; +} nr_stun_attr_data; + + +typedef struct nr_stun_encoded_attribute_ { + UINT2 type; + UINT2 length; + UCHAR value[NR_STUN_MAX_MESSAGE_SIZE]; +} nr_stun_encoded_attribute; + +typedef struct nr_stun_message_attribute_ { + UINT2 type; + UINT2 length; + union { + nr_transport_addr address; + nr_transport_addr alternate_server; + nr_stun_attr_error_code error_code; + nr_stun_attr_fingerprint fingerprint; + nr_transport_addr mapped_address; + nr_stun_attr_message_integrity message_integrity; + char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */ + char realm[NR_STUN_MAX_REALM_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_xor_mapped_address relay_address; + char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_unknown_attributes unknown_attributes; + char username[NR_STUN_MAX_USERNAME_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_xor_mapped_address xor_mapped_address; + +#ifdef USE_ICE + UINT4 priority; + UINT8 ice_controlled; + UINT8 ice_controlling; +#endif /* USE_ICE */ + +#ifdef USE_TURN + UINT4 lifetime_secs; + nr_transport_addr remote_address; + UCHAR requested_transport; + nr_stun_attr_data data; +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 + UINT4 change_request; +#endif /* USE_STUND_0_96 */ + + /* make sure there's enough room here to place any possible + * attribute */ + UCHAR largest_possible_attribute[NR_STUN_MAX_MESSAGE_SIZE]; + } u; + nr_stun_encoded_attribute *encoding; + int encoding_length; + char *name; + char *type_name; + int invalid; + TAILQ_ENTRY(nr_stun_message_attribute_) entry; +} nr_stun_message_attribute; + +typedef TAILQ_HEAD(nr_stun_message_attribute_head_,nr_stun_message_attribute_) nr_stun_message_attribute_head; + +typedef struct nr_stun_message_header_ { + UINT2 type; + UINT2 length; + UINT4 magic_cookie; + UINT12 id; +} nr_stun_message_header; + +typedef struct nr_stun_message_ { + char *name; + UCHAR buffer[NR_STUN_MAX_MESSAGE_SIZE]; + int length; + nr_stun_message_header header; + int comprehension_required_unknown_attributes; + int comprehension_optional_unknown_attributes; + nr_stun_message_attribute_head attributes; +} nr_stun_message; + +int nr_stun_message_create(nr_stun_message **msg); +int nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, int length); +int nr_stun_message_destroy(nr_stun_message **msg); + +int nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr); +int nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr); + +int nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute); + +int nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server); +int nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason); +int nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg); +int nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password); +int nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce); +int nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm); +int nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name); +int nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes); +int nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username); +int nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address); + +#ifdef USE_ICE +int nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled); +int nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling); +int nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority); +int nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg); +#endif /* USE_ICE */ + +#ifdef USE_TURN +int nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length); +int nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs); +int nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR transport); +int +nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address); +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 +int nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request); +#endif /* USE_STUND_0_96 */ + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_proc.c b/media/mtransport/third_party/nICEr/src/stun/stun_proc.c new file mode 100644 index 000000000..336100264 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_proc.c @@ -0,0 +1,568 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: stun_proc.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" +#include "stun_reg.h" +#include "registry.h" + +static int +nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res); + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */ +int +nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg) +{ + int _status; + nr_stun_message_attribute *attr; + +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* if this message was generated by an RFC 3489 impementation, + * the call to nr_is_stun_message will fail, so skip that + * check and puke elsewhere if the message can't be decoded */ + if (msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE + || msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE2) { +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + if (!nr_is_stun_message(msg->buffer, msg->length)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Not a STUN message"); + ABORT(R_REJECTED); + } +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + } +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + + if (req == 0) { + if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_REQUEST) { + r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message type: %03x", msg->header.type); + ABORT(R_REJECTED); + } + } + else { + if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_RESPONSE + && NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_ERROR_RESPONSE) { + r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message class: %03x", msg->header.type); + ABORT(R_REJECTED); + } + + if (NR_STUN_GET_TYPE_METHOD(req->header.type) != NR_STUN_GET_TYPE_METHOD(msg->header.type)) { + r_log(NR_LOG_STUN,LOG_WARNING,"Inconsistent message method: %03x expected %03x", msg->header.type, req->header.type); + ABORT(R_REJECTED); + } + + if (nr_stun_different_transaction(msg->buffer, msg->length, req)) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized STUN transaction"); + ABORT(R_REJECTED); + } + } + + switch (msg->header.magic_cookie) { + case NR_STUN_MAGIC_COOKIE: + /* basically draft-ietf-behave-rfc3489bis-10.txt S 6 rules */ + + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &attr) + && !attr->u.fingerprint.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Invalid fingerprint"); + ABORT(R_REJECTED); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + + default: +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ +#else +#ifdef NDEBUG + /* in deployment builds we should always see a recognized magic cookie */ + r_log(NR_LOG_STUN, LOG_WARNING, "Missing Magic Cookie"); + ABORT(R_REJECTED); +#else + /* ignore this condition because sometimes we like to pretend we're + * a server talking to old clients and their messages don't contain + * a magic cookie at all but rather the magic cookie field is part + * of their ID and therefore random */ +#endif /* NDEBUG */ +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + break; + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1 */ +int +nr_stun_process_request(nr_stun_message *req, nr_stun_message *res) +{ + int _status; +#ifdef USE_STUN_PEDANTIC + int r; + nr_stun_attr_unknown_attributes unknown_attributes = { { 0 } }; + nr_stun_message_attribute *attr; + + if (req->comprehension_required_unknown_attributes > 0) { + nr_stun_form_error_response(req, res, 420, "Unknown Attributes"); + + TAILQ_FOREACH(attr, &req->attributes, entry) { + if (attr->name == 0) { + /* unrecognized attribute */ + + /* should never happen, but truncate if it ever were to occur */ + if (unknown_attributes.num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) + break; + + unknown_attributes.attribute[unknown_attributes.num_attributes++] = attr->type; + } + } + + assert(req->comprehension_required_unknown_attributes + req->comprehension_optional_unknown_attributes == unknown_attributes.num_attributes); + + if ((r=nr_stun_message_add_unknown_attributes_attribute(res, &unknown_attributes))) + ABORT(R_ALREADY); + + ABORT(R_ALREADY); + } +#endif /* USE_STUN_PEDANTIC */ + + _status=0; +#ifdef USE_STUN_PEDANTIC + abort: +#endif /* USE_STUN_PEDANTIC */ + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.2 */ +int +nr_stun_process_indication(nr_stun_message *ind) +{ + int _status; +#ifdef USE_STUN_PEDANTIC + + if (ind->comprehension_required_unknown_attributes > 0) + ABORT(R_REJECTED); +#endif /* USE_STUN_PEDANTIC */ + + _status=0; +#ifdef USE_STUN_PEDANTIC + abort: +#endif /* USE_STUN_PEDANTIC */ + return _status; +} + +/* RFC5389 S 7.3.3, except that we *also* allow a MAPPED_ADDRESS + to compensate for a bug in Google's STUN server where it + always returns MAPPED_ADDRESS. + + Mozilla bug: 888274. + */ +int +nr_stun_process_success_response(nr_stun_message *res) +{ + int _status; + +#ifdef USE_STUN_PEDANTIC + if (res->comprehension_required_unknown_attributes > 0) + ABORT(R_REJECTED); +#endif /* USE_STUN_PEDANTIC */ + + if (NR_STUN_GET_TYPE_METHOD(res->header.type) == NR_METHOD_BINDING) { + if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0) && + ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing XOR-MAPPED-ADDRESS and MAPPED_ADDRESS"); + ABORT(R_REJECTED); + } + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.4 */ +int +nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code) +{ + int _status; + nr_stun_message_attribute *attr; + + if (res->comprehension_required_unknown_attributes > 0) { + ABORT(R_REJECTED); + } + + if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, &attr)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing ERROR-CODE"); + ABORT(R_REJECTED); + } + + *error_code = attr->u.error_code.number; + + switch (attr->u.error_code.number / 100) { + case 3: + /* If the error code is 300 through 399, the client SHOULD consider + * the transaction as failed unless the ALTERNATE-SERVER extension is + * being used. See Section 11. */ + + if (attr->u.error_code.number == 300) { + if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ALTERNATE_SERVER, 0)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing ALTERNATE-SERVER"); + ABORT(R_REJECTED); + } + + /* draft-ietf-behave-rfc3489bis-10.txt S 11 */ + if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + + ABORT(R_RETRY); + } + + ABORT(R_REJECTED); + break; + + case 4: + /* If the error code is 400 through 499, the client declares the + * transaction failed; in the case of 420 (Unknown Attribute), the + * response should contain a UNKNOWN-ATTRIBUTES attribute that gives + * additional information. */ + if (attr->u.error_code.number == 420) + ABORT(R_REJECTED); + + /* it may be possible to restart given the info that was received in + * this response, so retry */ + ABORT(R_RETRY); + break; + + case 5: + /* If the error code is 500 through 599, the client MAY resend the + * request; clients that do so MUST limit the number of times they do + * this. */ + /* let the retransmit mechanism handle resending the request */ + break; + + default: + ABORT(R_REJECTED); + break; + } + + /* the spec says: "The client then does any processing specified by the authentication + * mechanism (see Section 10). This may result in a new transaction + * attempt." -- but this is handled already elsewhere, so needn't be repeated + * in this function */ + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.2 */ +int +nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg, + nr_stun_message *res) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (msg->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + nr_stun_form_error_response(msg, res, 400, "Missing MESSAGE-INTEGRITY"); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, 0)) { + nr_stun_form_error_response(msg, res, 400, "Missing USERNAME"); + ABORT(R_ALREADY); + } + + if (attr->u.message_integrity.unknown_user) { + nr_stun_form_error_response(msg, res, 401, "Unrecognized USERNAME"); + ABORT(R_ALREADY); + } + + if (!attr->u.message_integrity.valid) { + nr_stun_form_error_response(msg, res, 401, "Bad MESSAGE-INTEGRITY"); + ABORT(R_ALREADY); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.3 */ +int +nr_stun_receive_response_short_term_auth(nr_stun_message *res) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (res->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + + if (!attr->u.message_integrity.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + +static int +nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res) +{ + int r,_status; + char *realm = 0; + char *nonce; + UINT2 size; + + if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm))) + ABORT(r); + + if ((r=nr_stun_message_add_realm_attribute(res, realm))) + ABORT(r); + + if (clnt) { + if (strlen(clnt->nonce) < 1) + new_nonce = 1; + + if (new_nonce) { + if (NR_reg_get_uint2(NR_STUN_REG_PREF_SERVER_NONCE_SIZE, &size)) + size = 48; + + if (size > (sizeof(clnt->nonce) - 1)) + size = sizeof(clnt->nonce) - 1; + + nr_random_alphanum(clnt->nonce, size); + clnt->nonce[size] = '\0'; + } + + nonce = clnt->nonce; + } + else { + /* user is not known, so use a bogus nonce since there's no way to + * store a good nonce with the client-specific data -- this nonce + * will be recognized as stale if the client attempts another + * request */ + nonce = "STALE"; + } + + if ((r=nr_stun_message_add_nonce_attribute(res, nonce))) + ABORT(r); + + _status=0; + abort: +#ifdef USE_TURN +assert(_status == 0); /* TODO: !nn! cleanup after I reimplmement TURN */ +#endif + RFREE(realm); + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.1 - 10.2.2 */ +int +nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res) +{ + int r,_status; + nr_stun_message_attribute *mi; + nr_stun_message_attribute *n; + nr_stun_server_client *clnt = 0; + + switch (req->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)) { + nr_stun_form_error_response(req, res, 400, "Missing USERNAME"); + nr_stun_add_realm_and_nonce(0, 0, res); + ABORT(R_ALREADY); + } + + if ((r=nr_stun_get_message_client(ctx, req, &clnt))) { + nr_stun_form_error_response(req, res, 401, "Unrecognized USERNAME"); + nr_stun_add_realm_and_nonce(0, 0, res); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, &mi)) { + nr_stun_form_error_response(req, res, 401, "Missing MESSAGE-INTEGRITY"); + nr_stun_add_realm_and_nonce(0, clnt, res); + ABORT(R_ALREADY); + } + + assert(!mi->u.message_integrity.unknown_user); + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_REALM, 0)) { + nr_stun_form_error_response(req, res, 400, "Missing REALM"); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_NONCE, &n)) { + nr_stun_form_error_response(req, res, 400, "Missing NONCE"); + ABORT(R_ALREADY); + } + + assert(sizeof(clnt->nonce) == sizeof(n->u.nonce)); + if (strncmp(clnt->nonce, n->u.nonce, sizeof(n->u.nonce))) { + nr_stun_form_error_response(req, res, 438, "Stale NONCE"); + nr_stun_add_realm_and_nonce(1, clnt, res); + ABORT(R_ALREADY); + } + + if (!mi->u.message_integrity.valid) { + nr_stun_form_error_response(req, res, 401, "Bad MESSAGE-INTEGRITY"); + nr_stun_add_realm_and_nonce(0, clnt, res); + ABORT(R_ALREADY); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to do in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.3 */ +int +nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (res->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_REALM, &attr)) { + RFREE(ctx->realm); + ctx->realm = r_strdup(attr->u.realm); + if (!ctx->realm) + ABORT(R_NO_MEMORY); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing REALM"); + ABORT(R_REJECTED); + } + + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_NONCE, &attr)) { + RFREE(ctx->nonce); + ctx->nonce = r_strdup(attr->u.nonce); + if (!ctx->nonce) + ABORT(R_NO_MEMORY); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing NONCE"); + ABORT(R_REJECTED); + } + + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + if (!attr->u.message_integrity.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_proc.h b/media/mtransport/third_party/nICEr/src/stun/stun_proc.h new file mode 100644 index 000000000..597567077 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_proc.h @@ -0,0 +1,53 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_proc_h +#define _stun_proc_h + +#include "stun.h" + +int nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg); +int nr_stun_process_request(nr_stun_message *req, nr_stun_message *res); +int nr_stun_process_indication(nr_stun_message *ind); +int nr_stun_process_success_response(nr_stun_message *res); +int nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code); + +int nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg, nr_stun_message *res); +int nr_stun_receive_response_short_term_auth(nr_stun_message *res); + +int nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res); +int nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_reg.h b/media/mtransport/third_party/nICEr/src/stun/stun_reg.h new file mode 100644 index 000000000..2167fcd91 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_reg.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_reg_h +#define _stun_reg_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT "stun.client.retransmission_timeout" +#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF "stun.client.retransmission_backoff_factor" +#define NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS "stun.client.maximum_transmits" +#define NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF "stun.client.final_retransmit_backoff" + +#define NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS "stun.allow_loopback" +#define NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS "stun.allow_link_local" +#define NR_STUN_REG_PREF_ADDRESS_PRFX "stun.address" +#define NR_STUN_REG_PREF_SERVER_NAME "stun.server.name" +#define NR_STUN_REG_PREF_SERVER_NONCE_SIZE "stun.server.nonce_size" +#define NR_STUN_REG_PREF_SERVER_REALM "stun.server.realm" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c new file mode 100644 index 000000000..3bfa1674b --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c @@ -0,0 +1,472 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: stun_server_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <string.h> +#include <assert.h> + +#include "nr_api.h" +#include "stun.h" + +static int nr_stun_server_destroy_client(nr_stun_server_client *clnt); +static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt); +static int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res); + + +int nr_stun_server_ctx_create(char *label, nr_socket *sock, nr_stun_server_ctx **ctxp) + { + int r,_status; + nr_stun_server_ctx *ctx=0; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_server_ctx)))) + ABORT(R_NO_MEMORY); + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + ctx->sock=sock; + nr_socket_getaddr(sock,&ctx->my_addr); + + STAILQ_INIT(&ctx->clients); + + *ctxp=ctx; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp) + { + nr_stun_server_ctx *ctx; + nr_stun_server_client *clnt1,*clnt2; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + + STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) { + nr_stun_server_destroy_client(clnt1); + } + + nr_stun_server_destroy_client(ctx->default_client); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + +static int nr_stun_server_client_create(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg, nr_stun_server_client **clntp) + { + nr_stun_server_client *clnt=0; + int r,_status; + + if(!(clnt=RCALLOC(sizeof(nr_stun_server_client)))) + ABORT(R_NO_MEMORY); + + if(!(clnt->label=r_strdup(client_label))) + ABORT(R_NO_MEMORY); + + if(!(clnt->username=r_strdup(user))) + ABORT(R_NO_MEMORY); + + if(r=r_data_copy(&clnt->password,pass)) + ABORT(r); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user); + clnt->stun_server_cb=cb; + clnt->cb_arg=cb_arg; + + *clntp = clnt; + _status=0; + abort: + if(_status){ + nr_stun_server_destroy_client(clnt); + } + return(_status); + } + +int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg) + { + int r,_status; + nr_stun_server_client *clnt; + + if (r=nr_stun_server_client_create(ctx, client_label, user, pass, cb, cb_arg, &clnt)) + ABORT(r); + + STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry); + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg) + { + int r,_status; + nr_stun_server_client *clnt; + + assert(!ctx->default_client); + if (ctx->default_client) + ABORT(R_INTERNAL); + + if (r=nr_stun_server_client_create(ctx, "default_client", ufrag, pass, cb, cb_arg, &clnt)) + ABORT(r); + + ctx->default_client = clnt; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg) + { + nr_stun_server_client *clnt1,*clnt2; + int found = 0; + + STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) { + if(clnt1->cb_arg == cb_arg) { + STAILQ_REMOVE(&ctx->clients, clnt1, nr_stun_server_client_, entry); + nr_stun_server_destroy_client(clnt1); + found++; + } + } + + if (!found) + ERETURN(R_NOT_FOUND); + + return 0; + } + +static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password) + { + int _status; + nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg; + nr_stun_server_client *clnt = 0; + nr_stun_message_attribute *username_attribute; + + if ((nr_stun_get_message_client(ctx, msg, &clnt))) { + if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); + } + + /* Although this is an exceptional condition, we'll already have seen a + * NOTICE-level log message about the unknown user, so additional log + * messages at any level higher than DEBUG are unnecessary. */ + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username); + ABORT(R_NOT_FOUND); + } + + *password = &clnt->password; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res) + { + int r,_status; + + if (nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0) + || !(auth_rule & NR_STUN_AUTH_RULE_OPTIONAL)) { + /* favor long term credentials over short term, if both are supported */ + + if (auth_rule & NR_STUN_AUTH_RULE_LONG_TERM) { + if ((r=nr_stun_receive_request_long_term_auth(req, ctx, res))) + ABORT(r); + } + else if (auth_rule & NR_STUN_AUTH_RULE_SHORT_TERM) { + if ((r=nr_stun_receive_request_or_indication_short_term_auth(req, res))) + ABORT(r); + } + } + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule) + { + int r,_status; + char string[256]; + nr_stun_message *req = 0; + nr_stun_message *res = 0; + nr_stun_server_client *clnt = 0; + nr_stun_server_request info; + int error; + int dont_free = 0; + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string); + + snprintf(string, sizeof(string)-1, "STUN-SERVER(%s): Received ", ctx->label); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len); + + memset(&info,0,sizeof(info)); + + if ((r=nr_stun_message_create2(&req, (UCHAR*)msg, len))) + ABORT(r); + + if ((r=nr_stun_message_create(&res))) + ABORT(r); + + if ((r=nr_stun_decode_message(req, nr_stun_server_get_password, ctx))) { + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Failed to decode request"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + if ((r=nr_stun_receive_message(0, req))) { + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Section 7.3 check failed"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_REQUEST + && NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_INDICATION) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Illegal message type: %04x",ctx->label,req->header.type); + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Unsupported message type"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + /* "The STUN agent then does any checks that are required by a + * authentication mechanism that the usage has specified" */ + if ((r=nr_stun_server_process_request_auth_checks(ctx, req, auth_rule, res))) + ABORT(r); + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) { + if ((r=nr_stun_process_indication(req))) + ABORT(r); + } + else { + if ((r=nr_stun_process_request(req, res))) + ABORT(r); + } + + assert(res->header.type == 0); + + clnt = 0; + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_REQUEST) { + if ((nr_stun_get_message_client(ctx, req, &clnt))) { + if ((r=nr_stun_form_success_response(req, peer_addr, 0, res))) + ABORT(r); + } + else { + if ((r=nr_stun_form_success_response(req, peer_addr, &clnt->password, res))) + ABORT(r); + } + } + + if(clnt && clnt->stun_server_cb){ + r_log(NR_LOG_STUN,LOG_DEBUG,"Entering STUN server callback"); + + /* Set up the info */ + if(r=nr_transport_addr_copy(&info.src_addr,peer_addr)) + ABORT(r); + + info.request = req; + info.response = res; + + error = 0; + dont_free = 0; + if (clnt->stun_server_cb(clnt->cb_arg,ctx,sock,&info,&dont_free,&error)) { + if (error == 0) + error = 500; + + nr_stun_form_error_response(req, res, error, "ICE Failure"); + ABORT(R_ALREADY); + } + } + + _status=0; + abort: + if (!res) + goto skip_response; + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) + goto skip_response; + + /* Now respond */ + + if (_status != 0 && ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)) + nr_stun_form_error_response(req, res, 500, "Failed to specify error"); + + if ((r=nr_stun_server_send_response(ctx, sock, peer_addr, res, clnt))) { + r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Failed sending response (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string); + _status = R_FAILED; + } + +#if 0 + /* EKR: suppressed these checks because if you have an error when + you are sending an error, things go wonky */ +#ifdef SANITY_CHECKS + if (_status == R_ALREADY) { + assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_ERROR_RESPONSE); + assert(nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)); + } + else { + assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_RESPONSE); + assert(!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)); + } +#endif /* SANITY_CHECKS */ +#endif + + if (0) { + skip_response: + _status = 0; + } + + if (!dont_free) { + nr_stun_message_destroy(&res); + nr_stun_message_destroy(&req); + } + + return(_status); + } + +static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt) + { + int r,_status; + Data *hmacPassword; + char string[256]; + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(label=%s): Sending(my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string); + + if (clnt) { + hmacPassword = &clnt->password; + } + else { + hmacPassword = 0; + } + + if ((r=nr_stun_encode_message(res))) { + /* should never happen */ + r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Unable to encode message", ctx->label); + } + else { + snprintf(string, sizeof(string)-1, "STUN(%s): Sending to %s ", ctx->label, peer_addr->as_string); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)res->buffer, res->length); + + if(r=nr_socket_sendto(sock?sock:ctx->sock,res->buffer,res->length,0,peer_addr)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +static int nr_stun_server_destroy_client(nr_stun_server_client *clnt) + { + if (!clnt) + return 0; + + RFREE(clnt->label); + RFREE(clnt->username); + r_data_zfree(&clnt->password); + + RFREE(clnt); + return(0); + } + +int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **out) + { + int _status; + nr_stun_message_attribute *attr; + nr_stun_server_client *clnt=0; + + if (! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); + } + + STAILQ_FOREACH(clnt, &ctx->clients, entry) { + if (!strncmp(clnt->username, attr->u.username, + sizeof(attr->u.username))) + break; + } + + if (!clnt && ctx->default_client) { + /* If we can't find a specific client see if this matches the default, + which means that the username starts with our ufrag. + */ + char *colon = strchr(attr->u.username, ':'); + if (colon && !strncmp(ctx->default_client->username, + attr->u.username, + colon - attr->u.username)) { + clnt = ctx->default_client; + r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Falling back to default client, username=: %s",ctx->label,attr->u.username); + } + } + + if (!clnt) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Request from unknown user: %s",ctx->label,attr->u.username); + ABORT(R_NOT_FOUND); + } + + *out = clnt; + + _status=0; + abort: + return(_status); + } + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.h b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.h new file mode 100644 index 000000000..343e67606 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.h @@ -0,0 +1,82 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_server_ctx_h +#define _stun_server_ctx_h + +typedef struct nr_stun_server_ctx_ nr_stun_server_ctx; +typedef struct nr_stun_server_client_ nr_stun_server_client; + +#include "stun_util.h" + + +typedef struct nr_stun_server_request_{ + nr_transport_addr src_addr; + nr_stun_message *request; + nr_stun_message *response; +} nr_stun_server_request; + +typedef int (*nr_stun_server_cb)(void *cb_arg, struct nr_stun_server_ctx_ *ctx,nr_socket *sock,nr_stun_server_request *req, int *dont_free, int *error); + +struct nr_stun_server_client_ { + char *label; + char *username; + Data password; + nr_stun_server_cb stun_server_cb; + void *cb_arg; + char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */ + STAILQ_ENTRY(nr_stun_server_client_) entry; +}; + +typedef STAILQ_HEAD(nr_stun_server_client_head_, nr_stun_server_client_) nr_stun_server_client_head; + +struct nr_stun_server_ctx_ { + char *label; + nr_socket *sock; + nr_transport_addr my_addr; + nr_stun_server_client_head clients; + nr_stun_server_client *default_client; +}; + + +int nr_stun_server_ctx_create(char *label, nr_socket *sock, nr_stun_server_ctx **ctxp); +int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp); +int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg); +int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg); +int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg); +int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule); +int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **clnt); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_util.c b/media/mtransport/third_party/nICEr/src/stun/stun_util.c new file mode 100644 index 000000000..e347e542f --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_util.c @@ -0,0 +1,322 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +static char *RCSSTRING __UNUSED__="$Id: stun_util.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#include <errno.h> +#include <csi_platform.h> + +#ifdef WIN32 +#include <winsock2.h> +#include <stdlib.h> +#include <io.h> +#include <time.h> +#else /* UNIX */ +#include <string.h> +#endif /* end UNIX */ +#include <assert.h> + +#include "stun.h" +#include "stun_reg.h" +#include "registry.h" +#include "addrs.h" +#include "transport_addr_reg.h" +#include "nr_crypto.h" +#include "hex.h" + + +int NR_LOG_STUN = 0; + +int +nr_stun_startup(void) +{ + int r,_status; + + if ((r=r_log_register("stun", &NR_LOG_STUN))) + ABORT(r); + + _status=0; + abort: + return _status; +} + +int +nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to) +{ + int _status; + + switch (from->ip_version) { + case NR_IPV4: + nr_ip4_port_to_transport_addr( + (ntohl(from->u.addr4.sin_addr.s_addr) ^ magicCookie), + (ntohs(from->u.addr4.sin_port) ^ (magicCookie>>16)), + from->protocol, to); + break; + case NR_IPV6: + { + union { + unsigned char addr[16]; + UINT4 addr32[4]; + } maskedAddr; + + maskedAddr.addr32[0] = htonl(magicCookie); /* Passed in host byte order */ + memcpy(&maskedAddr.addr32[1], transactionId.octet, sizeof(transactionId)); + + /* We now have the mask in network byte order */ + /* Xor the address in network byte order */ + for (int i = 0; i < sizeof(maskedAddr); ++i) { + maskedAddr.addr[i] ^= from->u.addr6.sin6_addr.s6_addr[i]; + } + + nr_ip6_port_to_transport_addr( + (struct in6_addr*)&maskedAddr, + (ntohs(from->u.addr6.sin6_port) ^ (magicCookie>>16)), + from->protocol, to); + } + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + _status = 0; + abort: + return _status; +} + +int +nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r,_status; + //NR_registry *children = 0; + + *count = 0; + + if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count))) + if (r != R_NOT_FOUND) + ABORT(r); + + if (*count == 0) { + char allow_loopback; + char allow_link_local; + + if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback))) { + if (r == R_NOT_FOUND) + allow_loopback = 0; + else + ABORT(r); + } + + if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, &allow_link_local))) { + if (r == R_NOT_FOUND) + allow_link_local = 0; + else + ABORT(r); + } + + if ((r=nr_stun_get_addrs(addrs, maxaddrs, !allow_loopback, !allow_link_local, count))) + ABORT(r); + + goto done; + } + + if (*count >= maxaddrs) { + r_log(NR_LOG_STUN, LOG_INFO, "Address list truncated from %d to %d", *count, maxaddrs); + *count = maxaddrs; + } + +#if 0 + if (*count > 0) { + /* TODO(ekr@rtfm.com): Commented out 2012-07-26. + + This code is currently not used in Firefox and needs to be + ported to 64-bit */ + children = RCALLOC((*count + 10) * sizeof(*children)); + if (!children) + ABORT(R_NO_MEMORY); + + assert(sizeof(size_t) == sizeof(*count)); + + if ((r=NR_reg_get_children(NR_STUN_REG_PREF_ADDRESS_PRFX, children, (size_t)(*count + 10), (size_t*)count))) + ABORT(r); + + for (i = 0; i < *count; ++i) { + if ((r=nr_reg_get_transport_addr(children[i], 0, &addrs[i].addr))) + ABORT(r); + } + } +#endif + + done: + + _status=0; + abort: + //RFREE(children); + return _status; +} + +int +nr_stun_different_transaction(UCHAR *msg, int len, nr_stun_message *req) +{ + int _status; + nr_stun_message_header header; + char reqid[44]; + char msgid[44]; + int len2; + + if (sizeof(header) > len) + ABORT(R_FAILED); + + assert(sizeof(header.id) == sizeof(UINT12)); + + memcpy(&header, msg, sizeof(header)); + + if (memcmp(&req->header.id, &header.id, sizeof(header.id))) { + nr_nbin2hex((UCHAR*)&req->header.id, sizeof(req->header.id), reqid, sizeof(reqid), &len2); + nr_nbin2hex((UCHAR*)&header.id, sizeof(header.id), msgid, sizeof(msgid), &len2); + r_log(NR_LOG_STUN, LOG_DEBUG, "Mismatched message IDs %s/%s", reqid, msgid); + ABORT(R_NOT_FOUND); + } + + _status=0; + abort: + return _status; +} + +char* +nr_stun_msg_type(int type) +{ + char *ret = 0; + + switch (type) { + case NR_STUN_MSG_BINDING_REQUEST: + ret = "BINDING-REQUEST"; + break; + case NR_STUN_MSG_BINDING_INDICATION: + ret = "BINDING-INDICATION"; + break; + case NR_STUN_MSG_BINDING_RESPONSE: + ret = "BINDING-RESPONSE"; + break; + case NR_STUN_MSG_BINDING_ERROR_RESPONSE: + ret = "BINDING-ERROR-RESPONSE"; + break; + +#ifdef USE_TURN + case NR_STUN_MSG_ALLOCATE_REQUEST: + ret = "ALLOCATE-REQUEST"; + break; + case NR_STUN_MSG_ALLOCATE_RESPONSE: + ret = "ALLOCATE-RESPONSE"; + break; + case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE: + ret = "ALLOCATE-ERROR-RESPONSE"; + break; + case NR_STUN_MSG_REFRESH_REQUEST: + ret = "REFRESH-REQUEST"; + break; + case NR_STUN_MSG_REFRESH_RESPONSE: + ret = "REFRESH-RESPONSE"; + break; + case NR_STUN_MSG_REFRESH_ERROR_RESPONSE: + ret = "REFRESH-ERROR-RESPONSE"; + break; + case NR_STUN_MSG_SEND_INDICATION: + ret = "SEND-INDICATION"; + break; + case NR_STUN_MSG_DATA_INDICATION: + ret = "DATA-INDICATION"; + break; + case NR_STUN_MSG_PERMISSION_REQUEST: + ret = "PERMISSION-REQUEST"; + break; + case NR_STUN_MSG_PERMISSION_RESPONSE: + ret = "PERMISSION-RESPONSE"; + break; + case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE: + ret = "PERMISSION-ERROR-RESPONSE"; + break; +#endif /* USE_TURN */ + + default: + /* ret remains 0 */ + break; + } + + return ret; +} + +int +nr_random_alphanum(char *alphanum, int size) +{ + static char alphanums[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; + int i; + + nr_crypto_random_bytes((UCHAR*)alphanum, size); + + /* now convert from binary to alphanumeric */ + for (i = 0; i < size; ++i) + alphanum[i] = alphanums[(UCHAR)alphanum[i]]; + + return 0; +} diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_util.h b/media/mtransport/third_party/nICEr/src/stun/stun_util.h new file mode 100644 index 000000000..c57e84d12 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/stun_util.h @@ -0,0 +1,56 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_util_h +#define _stun_util_h + +#include "stun.h" +#include "local_addr.h" + +extern int NR_LOG_STUN; + +int nr_stun_startup(void); + +int nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to); + +int nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count); + +int nr_stun_different_transaction(UCHAR *msg, int len, nr_stun_message *req); + +char* nr_stun_msg_type(int type); + +int nr_random_alphanum(char *alphanum, int size); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c new file mode 100644 index 000000000..c873f6faf --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.c @@ -0,0 +1,1056 @@ +/* + Copyright (c) 2007, Adobe Systems, Incorporated + Copyright (c) 2013, Mozilla + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: turn_client_ctx.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; + +#ifdef USE_TURN + +#include <assert.h> +#include <string.h> + +#include "nr_api.h" +#include "r_time.h" +#include "async_timer.h" +#include "nr_socket_buffered_stun.h" +#include "stun.h" +#include "turn_client_ctx.h" + +int NR_LOG_TURN = 0; + +#define TURN_MAX_PENDING_BYTES 32000 + +#define TURN_RTO 100 /* Hardcoded RTO estimate */ +#define TURN_LIFETIME_REQUEST_SECONDS 3600 /* One hour */ +#define TURN_USECS_PER_S 1000000 +#define TURN_REFRESH_SLACK_SECONDS 10 /* How long before expiry to refresh + allocations/permissions. The RFC 5766 + Section 7 recommendation if 60 seconds, + but this is silly since the transaction + times out after about 5. */ +#define TURN_PERMISSION_LIFETIME_SECONDS 300 /* 5 minutes. From RFC 5766 2.3 */ + +// Set to enable a temporary fix that will run the TURN reservation keep-alive +// logic when data is received via a TURN relayed path: a DATA_INDICATION packet is received. +// TODO(pkerr@mozilla.com) This should be replace/removed when bug 935806 is implemented. +#define REFRESH_RESERVATION_ON_RECV 1 + +static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int type, + NR_async_cb success_cb, + NR_async_cb failure_cb, + nr_turn_stun_ctx **ctxp); +static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp); +static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg); +static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx, + char *realm, char *nonce); +static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg); +static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx **sctx); +static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx *sctx, + UINT4 lifetime); +static int nr_turn_permission_create(nr_turn_client_ctx *ctx, + nr_transport_addr *addr, + nr_turn_permission **permp); +static int nr_turn_permission_find(nr_turn_client_ctx *ctx, + nr_transport_addr *addr, + nr_turn_permission **permp); +static int nr_turn_permission_destroy(nr_turn_permission **permp); +static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg); +static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *cb); +static int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags); + + +/* nr_turn_stun_ctx functions */ +static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int mode, + NR_async_cb success_cb, + NR_async_cb error_cb, + nr_turn_stun_ctx **ctxp) +{ + nr_turn_stun_ctx *sctx = 0; + int r,_status; + char label[256]; + + if (!(sctx=RCALLOC(sizeof(nr_turn_stun_ctx)))) + ABORT(R_NO_MEMORY); + + /* TODO(ekr@rtfm.com): label by phase */ + snprintf(label, sizeof(label), "%s:%s", tctx->label, ":TURN"); + + if ((r=nr_stun_client_ctx_create(label, tctx->sock, &tctx->turn_server_addr, + TURN_RTO, &sctx->stun))) { + ABORT(r); + } + + /* Set the STUN auth parameters, but don't set authentication on. + For that we need the nonce, set in nr_turn_stun_set_auth_params. + */ + sctx->stun->auth_params.username=tctx->username; + INIT_DATA(sctx->stun->auth_params.password, + tctx->password->data, tctx->password->len); + + sctx->tctx=tctx; + sctx->success_cb=success_cb; + sctx->error_cb=error_cb; + sctx->mode=mode; + sctx->last_error_code=0; + + /* Add ourselves to the tctx's list */ + STAILQ_INSERT_TAIL(&tctx->stun_ctxs, sctx, entry); + *ctxp=sctx; + + _status=0; +abort: + if (_status) { + nr_turn_stun_ctx_destroy(&sctx); + } + return(_status); +} + +/* Note: this function does not pull us off the tctx's list. */ +static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp) +{ + nr_turn_stun_ctx *ctx; + + if (!ctxp || !*ctxp) + return 0; + + ctx = *ctxp; + *ctxp = 0; + + nr_stun_client_ctx_destroy(&ctx->stun); + RFREE(ctx->realm); + RFREE(ctx->nonce); + + RFREE(ctx); + + return 0; +} + +static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx, + char *realm, char *nonce) +{ + int _status; + + RFREE(ctx->realm); + RFREE(ctx->nonce); + + assert(realm); + if (!realm) + ABORT(R_BAD_ARGS); + ctx->realm=r_strdup(realm); + if (!ctx->realm) + ABORT(R_NO_MEMORY); + + assert(nonce); + if (!nonce) + ABORT(R_BAD_ARGS); + ctx->nonce=r_strdup(nonce); + if (!ctx->nonce) + ABORT(R_NO_MEMORY); + + RFREE(ctx->stun->realm); + ctx->stun->realm = r_strdup(ctx->realm); + if (!ctx->stun->realm) + ABORT(R_NO_MEMORY); + + ctx->stun->auth_params.realm = ctx->realm; + ctx->stun->auth_params.nonce = ctx->nonce; + ctx->stun->auth_params.authenticate = 1; /* May already be 1 */ + + _status=0; +abort: + return(_status); +} + +static int nr_turn_stun_ctx_start(nr_turn_stun_ctx *ctx) +{ + int r, _status; + nr_turn_client_ctx *tctx = ctx->tctx; + + if ((r=nr_stun_client_reset(ctx->stun))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't reset STUN", + tctx->label); + ABORT(r); + } + + if ((r=nr_stun_client_start(ctx->stun, ctx->mode, nr_turn_stun_ctx_cb, ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", + tctx->label); + ABORT(r); + } + + _status=0; +abort: + return _status; +} + +static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg) +{ + int r, _status; + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + ctx->last_error_code = ctx->stun->error_code; + + switch (ctx->stun->state) { + case NR_STUN_CLIENT_STATE_DONE: + /* Save the realm and nonce */ + if (ctx->stun->realm && (!ctx->tctx->realm || strcmp(ctx->stun->realm, + ctx->tctx->realm))) { + RFREE(ctx->tctx->realm); + ctx->tctx->realm = r_strdup(ctx->stun->realm); + if (!ctx->tctx->realm) + ABORT(R_NO_MEMORY); + } + if (ctx->stun->nonce && (!ctx->tctx->nonce || strcmp(ctx->stun->nonce, + ctx->tctx->nonce))) { + RFREE(ctx->tctx->nonce); + ctx->tctx->nonce = r_strdup(ctx->stun->nonce); + if (!ctx->tctx->nonce) + ABORT(R_NO_MEMORY); + } + + ctx->retry_ct=0; + ctx->success_cb(0, 0, ctx); + break; + + case NR_STUN_CLIENT_STATE_FAILED: + /* Special case: if this is an authentication error, + we retry once. This allows the 401/438 nonce retry + paradigm. After that, we fail */ + /* TODO(ekr@rtfm.com): 401 needs a #define */ + /* TODO(ekr@rtfm.com): Add alternate-server (Mozilla bug 857688) */ + if (ctx->stun->error_code == 401 || ctx->stun->error_code == 438) { + if (ctx->retry_ct > 0) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Exceeded the number of retries", ctx->tctx->label); + ABORT(R_FAILED); + } + + if (!ctx->stun->nonce) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no nonce", ctx->tctx->label); + ABORT(R_FAILED); + } + if (!ctx->stun->realm) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no realm", ctx->tctx->label); + ABORT(R_FAILED); + } + + /* Try to retry */ + if ((r=nr_turn_stun_set_auth_params(ctx, ctx->stun->realm, + ctx->stun->nonce))) + ABORT(r); + + ctx->stun->error_code = 0; /* Reset to avoid inf-looping */ + + if ((r=nr_turn_stun_ctx_start(ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", ctx->tctx->label); + ABORT(r); + } + + ctx->retry_ct++; + } + else { + ABORT(R_FAILED); + } + break; + + case NR_STUN_CLIENT_STATE_TIMED_OUT: + ABORT(R_FAILED); + break; + + case NR_STUN_CLIENT_STATE_CANCELLED: + assert(0); /* Shouldn't happen */ + return; + break; + + default: + assert(0); /* Shouldn't happen */ + return; + } + + _status=0; +abort: + if (_status) { + ctx->error_cb(0, 0, ctx); + } +} + +/* nr_turn_client_ctx functions */ +int nr_turn_client_ctx_create(const char *label, nr_socket *sock, + const char *username, Data *password, + nr_transport_addr *addr, + nr_turn_client_ctx **ctxp) +{ + nr_turn_client_ctx *ctx=0; + int r,_status; + + if ((r=r_log_register("turn", &NR_LOG_TURN))) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_turn_client_ctx)))) + ABORT(R_NO_MEMORY); + + STAILQ_INIT(&ctx->stun_ctxs); + STAILQ_INIT(&ctx->permissions); + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + ctx->sock=sock; + ctx->username = r_strdup(username); + if (!ctx->username) + ABORT(R_NO_MEMORY); + + if ((r=r_data_create(&ctx->password, password->data, password->len))) + ABORT(r); + if ((r=nr_transport_addr_copy(&ctx->turn_server_addr, addr))) + ABORT(r); + + ctx->state = NR_TURN_CLIENT_STATE_INITTED; + if (addr->protocol == IPPROTO_TCP) { + if ((r=nr_socket_connect(ctx->sock, &ctx->turn_server_addr))) { + if (r != R_WOULDBLOCK) + ABORT(r); + } + } + + *ctxp=ctx; + + _status=0; +abort: + if(_status){ + nr_turn_client_ctx_destroy(&ctx); + } + return(_status); +} + +int +nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp) +{ + nr_turn_client_ctx *ctx; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + *ctxp = 0; + + if (ctx->label) + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): destroy", ctx->label); + + nr_turn_client_deallocate(ctx); + + /* Cancel frees the rest of our data */ + RFREE(ctx->label); + ctx->label = 0; + + nr_turn_client_cancel(ctx); + + RFREE(ctx->username); + ctx->username = 0; + r_data_destroy(&ctx->password); + RFREE(ctx->nonce); + ctx->nonce = 0; + RFREE(ctx->realm); + ctx->realm = 0; + + /* Destroy the STUN client ctxs */ + while (!STAILQ_EMPTY(&ctx->stun_ctxs)) { + nr_turn_stun_ctx *stun = STAILQ_FIRST(&ctx->stun_ctxs); + STAILQ_REMOVE_HEAD(&ctx->stun_ctxs, entry); + nr_turn_stun_ctx_destroy(&stun); + } + + /* Destroy the permissions */ + while (!STAILQ_EMPTY(&ctx->permissions)) { + nr_turn_permission *perm = STAILQ_FIRST(&ctx->permissions); + STAILQ_REMOVE_HEAD(&ctx->permissions, entry); + nr_turn_permission_destroy(&perm); + } + + RFREE(ctx); + + return(0); +} + +int nr_turn_client_cancel(nr_turn_client_ctx *ctx) +{ + nr_turn_stun_ctx *stun = 0; + + if (ctx->state == NR_TURN_CLIENT_STATE_CANCELLED || + ctx->state == NR_TURN_CLIENT_STATE_FAILED) + return(0); + + if (ctx->label) + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): cancelling", ctx->label); + + /* Cancel the STUN client ctxs */ + stun = STAILQ_FIRST(&ctx->stun_ctxs); + while (stun) { + nr_stun_client_cancel(stun->stun); + stun = STAILQ_NEXT(stun, entry); + } + + /* Cancel the timers, if not already cancelled */ + NR_async_timer_cancel(ctx->connected_timer_handle); + NR_async_timer_cancel(ctx->refresh_timer_handle); + + ctx->state = NR_TURN_CLIENT_STATE_CANCELLED; + + return(0); +} + +int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags) +{ + int r,_status; + + if ((r=nr_stun_encode_message(req))) + ABORT(r); + + if ((r=nr_socket_sendto(ctx->sock, + req->buffer, req->length, flags, + &ctx->turn_server_addr))) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Failed sending request", + ctx->label); + ABORT(r); + } + + _status=0; +abort: + return(_status); +} + +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx) +{ + int r,_status; + nr_stun_message *aloc = 0; + nr_stun_client_auth_params auth; + nr_stun_client_refresh_request_params refresh; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + return(0); + + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): deallocating", ctx->label); + + refresh.lifetime_secs = 0; + + auth.username = ctx->username; + INIT_DATA(auth.password, ctx->password->data, ctx->password->len); + + auth.realm = ctx->realm; + auth.nonce = ctx->nonce; + + auth.authenticate = 1; + + if ((r=nr_stun_build_refresh_request(&auth, &refresh, &aloc))) + ABORT(r); + + // We are only sending a single request here because we are in the process of + // shutting everything down. Theoretically we should probably start a seperate + // STUN transaction which outlives the TURN context. + if ((r=nr_turn_client_send_stun_request(ctx, aloc, 0))) + ABORT(r); + + ctx->state = NR_TURN_CLIENT_STATE_DEALLOCATING; + + _status=0; +abort: + nr_stun_message_destroy(&aloc); + return(_status); +} + +static void nr_turn_client_fire_finished_cb(nr_turn_client_ctx *ctx) + { + if (ctx->finished_cb) { + NR_async_cb finished_cb=ctx->finished_cb; + ctx->finished_cb=0; + finished_cb(0, 0, ctx->cb_arg); + } + } + +int nr_turn_client_failed(nr_turn_client_ctx *ctx) +{ + if (ctx->state == NR_TURN_CLIENT_STATE_FAILED || + ctx->state == NR_TURN_CLIENT_STATE_CANCELLED) + return(0); + + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s) failed", ctx->label); + nr_turn_client_cancel(ctx); + ctx->state = NR_TURN_CLIENT_STATE_FAILED; + nr_turn_client_fire_finished_cb(ctx); + + return(0); +} + +int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx, + nr_transport_addr *relayed_address) +{ + int r, _status; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + if (r=nr_transport_addr_copy(relayed_address, &ctx->relay_addr)) + ABORT(r); + + _status=0; +abort: + return(_status); +} + +int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx, + nr_transport_addr *mapped_address) +{ + int r, _status; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + if (r=nr_transport_addr_copy(mapped_address, &ctx->mapped_addr)) + ABORT(r); + + _status=0; +abort: + return(_status); +} + +static void nr_turn_client_allocate_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + nr_turn_stun_ctx *refresh_ctx; + int r,_status; + + ctx->tctx->state = NR_TURN_CLIENT_STATE_ALLOCATED; + + if ((r=nr_transport_addr_copy( + &ctx->tctx->relay_addr, + &ctx->stun->results.allocate_response.relay_addr))) + ABORT(r); + + if ((r=nr_transport_addr_copy( + &ctx->tctx->mapped_addr, + &ctx->stun->results.allocate_response.mapped_addr))) + ABORT(r); + + if ((r=nr_turn_client_refresh_setup(ctx->tctx, &refresh_ctx))) + ABORT(r); + + if ((r=nr_turn_client_start_refresh_timer( + ctx->tctx, refresh_ctx, + ctx->stun->results.allocate_response.lifetime_secs))) + ABORT(r); + + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): Succesfully allocated addr %s lifetime=%u", + ctx->tctx->label, + ctx->tctx->relay_addr.as_string, + ctx->stun->results.allocate_response.lifetime_secs); + + nr_turn_client_fire_finished_cb(ctx->tctx); + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } +} + +static void nr_turn_client_error_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, %s", + ctx->tctx->label, ctx->mode, __FUNCTION__); + + nr_turn_client_failed(ctx->tctx); +} + +static void nr_turn_client_permission_error_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + if (ctx->last_error_code == 403) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, permission denied", + ctx->tctx->label, ctx->mode); + + } else{ + nr_turn_client_error_cb(0, 0, ctx); + } +} + +int nr_turn_client_allocate(nr_turn_client_ctx *ctx, + NR_async_cb finished_cb, void *cb_arg) +{ + nr_turn_stun_ctx *stun = 0; + int r,_status; + + if(ctx->state == NR_TURN_CLIENT_STATE_FAILED || + ctx->state == NR_TURN_CLIENT_STATE_CANCELLED){ + /* TURN TCP contexts can fail before we ever try to form an allocation, + * since the TCP connection can fail. It is also conceivable that a TURN + * TCP context could be cancelled before we are done forming all + * allocations (although we do not do this at the time this code was + * written) */ + assert(ctx->turn_server_addr.protocol == IPPROTO_TCP); + ABORT(R_NOT_FOUND); + } + + assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED); + + ctx->finished_cb=finished_cb; + ctx->cb_arg=cb_arg; + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST, + nr_turn_client_allocate_cb, + nr_turn_client_error_cb, + &stun))) + ABORT(r); + stun->stun->params.allocate_request.lifetime_secs = + TURN_LIFETIME_REQUEST_SECONDS; + + if (ctx->state == NR_TURN_CLIENT_STATE_INITTED) { + if ((r=nr_turn_stun_ctx_start(stun))) + ABORT(r); + ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING; + } else { + ABORT(R_ALREADY); + } + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx); + } + + return(_status); +} + +int nr_turn_client_process_response(nr_turn_client_ctx *ctx, + UCHAR *msg, int len, + nr_transport_addr *turn_server_addr) +{ + int r, _status; + nr_turn_stun_ctx *sc1; + + switch (ctx->state) { + case NR_TURN_CLIENT_STATE_ALLOCATING: + case NR_TURN_CLIENT_STATE_ALLOCATED: + break; + default: + ABORT(R_FAILED); + } + + sc1 = STAILQ_FIRST(&ctx->stun_ctxs); + while (sc1) { + r = nr_stun_client_process_response(sc1->stun, msg, len, turn_server_addr); + if (!r) + break; + if (r==R_RETRY) /* Likely a 401 and we will retry */ + break; + if (r != R_REJECTED) + ABORT(r); + sc1 = STAILQ_NEXT(sc1, entry); + } + if (!sc1) + ABORT(R_REJECTED); + + _status=0; +abort: + return(_status); +} + +static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx **sctx) +{ + nr_turn_stun_ctx *stun = 0; + int r,_status; + + assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED); + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_NOT_PERMITTED); + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_REFRESH_REQUEST, + nr_turn_client_refresh_cb, + nr_turn_client_error_cb, + &stun))) + ABORT(r); + + if ((r=nr_turn_stun_set_auth_params(stun, ctx->realm, ctx->nonce))) + ABORT(r); + + stun->stun->params.refresh_request.lifetime_secs = + TURN_LIFETIME_REQUEST_SECONDS; + + + *sctx=stun; + + _status=0; +abort: + return(_status); +} + +static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *tctx, + nr_turn_stun_ctx *sctx, + UINT4 lifetime) +{ + int _status; + + assert(!tctx->refresh_timer_handle); + + if (lifetime <= TURN_REFRESH_SLACK_SECONDS) { + r_log(NR_LOG_TURN, LOG_ERR, "Too short lifetime specified for turn %u", lifetime); + ABORT(R_BAD_DATA); + } + + if (lifetime > 3600) + lifetime = 3600; + + lifetime -= TURN_REFRESH_SLACK_SECONDS; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Setting refresh timer for %u seconds", + tctx->label, lifetime); + NR_ASYNC_TIMER_SET(lifetime * 1000, nr_turn_client_refresh_timer_cb, sctx, + &tctx->refresh_timer_handle); + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(tctx); + } + return _status; +} + +static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + int r,_status; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh timer fired", + ctx->tctx->label); + + ctx->tctx->refresh_timer_handle=0; + if ((r=nr_turn_stun_ctx_start(ctx))) { + ABORT(r); + } + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } + return; +} + +static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg) +{ + int r, _status; + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + /* Save lifetime from the reset */ + UINT4 lifetime = ctx->stun->results.refresh_response.lifetime_secs; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh succeeded. lifetime=%u", + ctx->tctx->label, lifetime); + + if ((r=nr_turn_client_start_refresh_timer( + ctx->tctx, ctx, lifetime))) + ABORT(r); + + _status=0; + +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } +} + +/* TODO(ekr@rtfm.com): We currently don't support channels. + We might in the future. Mozilla bug 857736 */ +int nr_turn_client_send_indication(nr_turn_client_ctx *ctx, + const UCHAR *msg, size_t len, + int flags, nr_transport_addr *remote_addr) +{ + int r,_status; + nr_stun_client_send_indication_params params = { { 0 } }; + nr_stun_message *ind = 0; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Send indication len=%zu", + ctx->label, len); + + if ((r=nr_turn_client_ensure_perm(ctx, remote_addr))) + ABORT(r); + + if ((r=nr_transport_addr_copy(¶ms.remote_addr, remote_addr))) + ABORT(r); + + params.data.data = (UCHAR*)msg; + params.data.len = len; + + if ((r=nr_stun_build_send_indication(¶ms, &ind))) + ABORT(r); + + if ((r=nr_turn_client_send_stun_request(ctx, ind, flags))) + ABORT(r); + + _status=0; +abort: + nr_stun_message_destroy(&ind); + return(_status); +} + +int nr_turn_client_parse_data_indication(nr_turn_client_ctx *ctx, + nr_transport_addr *source_addr, + UCHAR *msg, size_t len, + UCHAR *newmsg, size_t *newlen, + size_t newsize, + nr_transport_addr *remote_addr) +{ + int r,_status; + nr_stun_message *ind=0; + nr_stun_message_attribute *attr; + nr_turn_permission *perm; + + if (nr_transport_addr_cmp(&ctx->turn_server_addr, source_addr, + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(NR_LOG_TURN, LOG_WARNING, + "TURN(%s): Indication from unexpected source addr %s (expected %s)", + ctx->label, source_addr->as_string, ctx->turn_server_addr.as_string); + ABORT(R_REJECTED); + } + + if ((r=nr_stun_message_create2(&ind, msg, len))) + ABORT(r); + if ((r=nr_stun_decode_message(ind, 0, 0))) + ABORT(r); + + if (ind->header.type != NR_STUN_MSG_DATA_INDICATION) + ABORT(R_BAD_ARGS); + + if (!nr_stun_message_has_attribute(ind, NR_STUN_ATTR_XOR_PEER_ADDRESS, &attr)) + ABORT(R_BAD_ARGS); + + if ((r=nr_turn_permission_find(ctx, &attr->u.xor_mapped_address.unmasked, + &perm))) { + if (r == R_NOT_FOUND) { + r_log(NR_LOG_TURN, LOG_WARNING, + "TURN(%s): Indication from peer addr %s with no permission", + ctx->label, attr->u.xor_mapped_address.unmasked.as_string); + } + ABORT(r); + } + + if ((r=nr_transport_addr_copy(remote_addr, + &attr->u.xor_mapped_address.unmasked))) + ABORT(r); + +#if REFRESH_RESERVATION_ON_RECV + if ((r=nr_turn_client_ensure_perm(ctx, remote_addr))) { + ABORT(r); + } +#endif + + if (!nr_stun_message_has_attribute(ind, NR_STUN_ATTR_DATA, &attr)) { + ABORT(R_BAD_DATA); + } + + assert(newsize >= attr->u.data.length); + if (newsize < attr->u.data.length) + ABORT(R_BAD_ARGS); + + memcpy(newmsg, attr->u.data.data, attr->u.data.length); + *newlen = attr->u.data.length; + + _status=0; +abort: + nr_stun_message_destroy(&ind); + return(_status); +} + + + +/* The permissions model is as follows: + + - We keep a list of all the permissions we have ever requested + along with when they were last established. + - Whenever someone sends a packet, we automatically create/ + refresh the permission. + + This means that permissions automatically time out if + unused. + +*/ +int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx, nr_transport_addr *addr) +{ + int r, _status; + nr_turn_permission *perm = 0; + UINT8 now; + UINT8 turn_permission_refresh = (TURN_PERMISSION_LIFETIME_SECONDS - + TURN_REFRESH_SLACK_SECONDS) * TURN_USECS_PER_S; + + if ((r=nr_turn_permission_find(ctx, addr, &perm))) { + if (r == R_NOT_FOUND) { + if ((r=nr_turn_permission_create(ctx, addr, &perm))) + ABORT(r); + } + else { + ABORT(r); + } + } + + assert(perm); + + /* Now check that the permission is up-to-date */ + now = r_gettimeint(); + + if ((now - perm->last_used) > turn_permission_refresh) { + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Permission for %s requires refresh", + ctx->label, perm->addr.as_string); + + if ((r=nr_turn_stun_ctx_start(perm->stun))) + ABORT(r); + + perm->last_used = now; /* Update the time now so we don't retry on + next packet */ + } + + _status=0; +abort: + return(_status); +} + +static int nr_turn_permission_create(nr_turn_client_ctx *ctx, nr_transport_addr *addr, + nr_turn_permission **permp) +{ + int r, _status; + nr_turn_permission *perm = 0; + + assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED); + + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): Creating permission for %s", + ctx->label, addr->as_string); + + if (!(perm = RCALLOC(sizeof(nr_turn_permission)))) + ABORT(R_NO_MEMORY); + + if ((r=nr_transport_addr_copy(&perm->addr, addr))) + ABORT(r); + + perm->last_used = 0; + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_PERMISSION_REQUEST, + nr_turn_client_permissions_cb, + nr_turn_client_permission_error_cb, + &perm->stun))) + ABORT(r); + + /* We want to authenticate on the first packet */ + if ((r=nr_turn_stun_set_auth_params(perm->stun, ctx->realm, ctx->nonce))) + ABORT(r); + + if ((r=nr_transport_addr_copy( + &perm->stun->stun->params.permission_request.remote_addr, addr))) + ABORT(r); + STAILQ_INSERT_TAIL(&ctx->permissions, perm, entry); + + *permp = perm; + + _status=0; +abort: + if (_status) { + nr_turn_permission_destroy(&perm); + } + return(_status); +} + + +static int nr_turn_permission_find(nr_turn_client_ctx *ctx, nr_transport_addr *addr, + nr_turn_permission **permp) +{ + nr_turn_permission *perm; + int _status; + + perm = STAILQ_FIRST(&ctx->permissions); + while (perm) { + if (!nr_transport_addr_cmp(&perm->addr, addr, + NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + break; + + perm = STAILQ_NEXT(perm, entry); + } + + if (!perm) { + ABORT(R_NOT_FOUND); + } + if (perm->stun->last_error_code == 403) { + ABORT(R_NOT_PERMITTED); + } + *permp = perm; + + _status=0; +abort: + return(_status); +} + +static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Successfully refreshed permission", + ctx->tctx->label); +} + +/* Note that we don't destroy the nr_turn_stun_ctx. That is owned by the + nr_turn_client_ctx. */ +static int nr_turn_permission_destroy(nr_turn_permission **permp) +{ + nr_turn_permission *perm; + + if (!permp || !*permp) + return(0); + + perm = *permp; + *permp = 0; + + RFREE(perm); + + return(0); +} + +#endif /* USE_TURN */ diff --git a/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h new file mode 100644 index 000000000..c50e79d3b --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/stun/turn_client_ctx.h @@ -0,0 +1,137 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _turn_client_ctx_h +#define _turn_client_ctx_h + +/* + Represents a single set of STUN transactions, i.e., + Allocate, Refresh, Permission. It automatically handles + permissions and restarts. + */ +typedef struct nr_turn_stun_ctx_ { + struct nr_turn_client_ctx_ *tctx; + int mode; /* From stun_client_ctx.h, NR_TURN_CLIENT_MODE_* */ + int retry_ct; + nr_stun_client_ctx *stun; + char *nonce; + char *realm; + NR_async_cb success_cb; + NR_async_cb error_cb; + int last_error_code; + + STAILQ_ENTRY(nr_turn_stun_ctx_) entry; +} nr_turn_stun_ctx; +typedef STAILQ_HEAD(nr_turn_stun_ctx_head_, nr_turn_stun_ctx_) + nr_turn_stun_ctx_head; + +/* Represents a single TURN permission */ +typedef struct nr_turn_permission_ { + nr_transport_addr addr; + nr_turn_stun_ctx *stun; + UINT8 last_used; + + STAILQ_ENTRY(nr_turn_permission_) entry; +} nr_turn_permission; +typedef STAILQ_HEAD(nr_turn_permission_head_, nr_turn_permission_) + nr_turn_permission_head; + +/* A single connection to a TURN server. Use one + turn_client_ctx per socket/server pair. */ +typedef struct nr_turn_client_ctx_ { + int state; +#define NR_TURN_CLIENT_STATE_INITTED 1 +#define NR_TURN_CLIENT_STATE_ALLOCATING 2 +#define NR_TURN_CLIENT_STATE_ALLOCATED 3 +#define NR_TURN_CLIENT_STATE_FAILED 4 +#define NR_TURN_CLIENT_STATE_CANCELLED 5 +#define NR_TURN_CLIENT_STATE_DEALLOCATING 6 + + char *label; + nr_socket *sock; + + char *username; + Data *password; + char *nonce; + char *realm; + + nr_transport_addr turn_server_addr; + nr_transport_addr relay_addr; + nr_transport_addr mapped_addr; + + nr_turn_stun_ctx_head stun_ctxs; + nr_turn_permission_head permissions; + + NR_async_cb finished_cb; + void *cb_arg; + + void *connected_timer_handle; + void *refresh_timer_handle; +} nr_turn_client_ctx; + +extern int NR_LOG_TURN; + +int nr_turn_client_ctx_create(const char *label, nr_socket *sock, + const char *username, Data *password, + nr_transport_addr *addr, + nr_turn_client_ctx **ctxp); +int nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp); +int nr_turn_client_allocate(nr_turn_client_ctx *ctx, + NR_async_cb finished_cb, void *cb_arg); +int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx, + nr_transport_addr *relayed_address); +int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx, + nr_transport_addr *mapped_address); +int nr_turn_client_process_response(nr_turn_client_ctx *ctx, + UCHAR *msg, int len, + nr_transport_addr *turn_server_addr); +int nr_turn_client_cancel(nr_turn_client_ctx *ctx); +int nr_turn_client_failed(nr_turn_client_ctx *ctx); +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx); +int nr_turn_client_send_indication(nr_turn_client_ctx *ctx, + const UCHAR *msg, size_t len, + int flags, nr_transport_addr *remote_addr); +int nr_turn_client_parse_data_indication(nr_turn_client_ctx *ctx, + nr_transport_addr *source_addr, + UCHAR *msg, size_t len, + UCHAR *newmsg, size_t *newlen, + size_t newsize, + nr_transport_addr *remote_addr); +int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx, + nr_transport_addr *addr); +#endif + diff --git a/media/mtransport/third_party/nICEr/src/util/cb_args.c b/media/mtransport/third_party/nICEr/src/util/cb_args.c new file mode 100644 index 000000000..964f1abee --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/util/cb_args.c @@ -0,0 +1,61 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +static char *RCSSTRING __UNUSED__="$Id: cb_args.c,v 1.2 2008/04/28 17:59:04 ekr Exp $"; + +#include <stdarg.h> +#include "nr_api.h" +#include "cb_args.h" + +void **nr_pack_cb_args(int ct,...) + { + void **vlist; + va_list ap; + int i; + + va_start(ap,ct); + if(!(vlist=RCALLOC(sizeof(void *)*ct+1))) + abort(); + + for(i=0;i<ct;i++){ + vlist[i]=va_arg(ap, void *); + } + + va_end(ap); + + return(vlist); + } + + + diff --git a/media/mtransport/third_party/nICEr/src/util/cb_args.h b/media/mtransport/third_party/nICEr/src/util/cb_args.h new file mode 100644 index 000000000..83f7831cc --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/util/cb_args.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _cb_args_h +#define _cb_args_h + +void **nr_pack_cb_args(int ct,...); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/util/ice_util.c b/media/mtransport/third_party/nICEr/src/util/ice_util.c new file mode 100644 index 000000000..047be6659 --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/util/ice_util.c @@ -0,0 +1,76 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <stdarg.h> + + +static char *RCSSTRING __UNUSED__="$Id: ice_util.c,v 1.2 2008/04/28 17:59:05 ekr Exp $"; + +#include <stdarg.h> +#include <string.h> +#include "nr_api.h" +#include "ice_util.h" + +int nr_concat_strings(char **outp,...) + { + va_list ap; + char *s,*out=0; + int len=0; + int _status; + + va_start(ap,outp); + while(s=va_arg(ap,char *)){ + len+=strlen(s); + } + va_end(ap); + + + if(!(out=RMALLOC(len+1))) + ABORT(R_NO_MEMORY); + + *outp=out; + + va_start(ap,outp); + while(s=va_arg(ap,char *)){ + len=strlen(s); + memcpy(out,s,len); + out+=len; + } + va_end(ap); + + *out=0; + + _status=0; + abort: + return(_status); + } + diff --git a/media/mtransport/third_party/nICEr/src/util/ice_util.h b/media/mtransport/third_party/nICEr/src/util/ice_util.h new file mode 100644 index 000000000..44751edaf --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/util/ice_util.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _ice_util_h +#define _ice_util_h + +int nr_concat_strings(char **outp,...); + +#endif + diff --git a/media/mtransport/third_party/nICEr/src/util/mbslen.c b/media/mtransport/third_party/nICEr/src/util/mbslen.c new file mode 100644 index 000000000..3645c95cf --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/util/mbslen.c @@ -0,0 +1,139 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifdef LINUX +#define _GNU_SOURCE 1 +#endif +#include <string.h> + +#include <errno.h> +#include <csi_platform.h> + +#include <assert.h> +#include <locale.h> +#include <stdlib.h> +#include <wchar.h> +#if defined(DARWIN) || defined(__DragonFly__) || defined(__FreeBSD__) +#define HAVE_XLOCALE +#endif + +#ifdef HAVE_XLOCALE +#include <xlocale.h> +#endif /* HAVE_XLOCALE */ + +#include "nr_api.h" +#include "mbslen.h" + +/* get number of characters in a mult-byte character string */ +int +mbslen(const char *s, size_t *ncharsp) +{ +#ifdef HAVE_XLOCALE + static locale_t loc = 0; + static int initialized = 0; +#endif /* HAVE_XLOCALE */ +#ifdef WIN32 + char *my_locale=0; + unsigned int i; +#endif /* WIN32 */ + int _status; + size_t nbytes; + int nchars; + mbstate_t mbs; + +#ifdef HAVE_XLOCALE + if (! initialized) { + initialized = 1; + loc = newlocale(LC_CTYPE_MASK, "UTF-8", LC_GLOBAL_LOCALE); + } + + if (loc == 0) { + /* unable to create the UTF-8 locale */ + assert(loc != 0); /* should never happen */ +#endif /* HAVE_XLOCALE */ + +#ifdef WIN32 + if (!setlocale(LC_CTYPE, 0)) + ABORT(R_INTERNAL); + + if (!(my_locale = r_strdup(setlocale(LC_CTYPE, 0)))) + ABORT(R_NO_MEMORY); + + for (i=0; i<strlen(my_locale); i++) + my_locale[i] = toupper(my_locale[i]); + + if (!strstr(my_locale, "UTF-8") && !strstr(my_locale, "UTF8")) + ABORT(R_NOT_FOUND); +#else + /* can't count UTF-8 characters with mbrlen if the locale isn't UTF-8 */ + /* null-checking setlocale is required because Android */ + char *locale = setlocale(LC_CTYPE, 0); + /* some systems use "utf8" instead of "UTF-8" like Fedora 17 */ + if (!locale || (!strcasestr(locale, "UTF-8") && !strcasestr(locale, "UTF8"))) + ABORT(R_NOT_FOUND); +#endif + +#ifdef HAVE_XLOCALE + } +#endif /* HAVE_XLOCALE */ + + memset(&mbs, 0, sizeof(mbs)); + nchars = 0; + +#ifdef HAVE_XLOCALE + while (*s != '\0' && (nbytes = mbrlen_l(s, strlen(s), &mbs, loc)) != 0) +#else + while (*s != '\0' && (nbytes = mbrlen(s, strlen(s), &mbs)) != 0) +#endif /* HAVE_XLOCALE */ + { + if (nbytes == (size_t)-1) /* should never happen */ { + ABORT(R_INTERNAL); + } + if (nbytes == (size_t)-2) /* encoding error */ { + ABORT(R_BAD_DATA); + } + + s += nbytes; + ++nchars; + } + + *ncharsp = nchars; + + _status = 0; + abort: +#ifdef WIN32 + RFREE(my_locale); +#endif + return _status; +} + diff --git a/media/mtransport/third_party/nICEr/src/util/mbslen.h b/media/mtransport/third_party/nICEr/src/util/mbslen.h new file mode 100644 index 000000000..9a68e5dae --- /dev/null +++ b/media/mtransport/third_party/nICEr/src/util/mbslen.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _mbslen_h +#define _mbslen_h + +/* get number of characters in a mult-byte character string */ +int mbslen(const char *s, size_t *ncharsp); + +#endif + diff --git a/media/mtransport/third_party/nICEr/upstream.diff b/media/mtransport/third_party/nICEr/upstream.diff new file mode 100644 index 000000000..a664fd9d2 --- /dev/null +++ b/media/mtransport/third_party/nICEr/upstream.diff @@ -0,0 +1,2449 @@ +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.c src/ice/ice_candidate.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate.c 2012-10-06 08:30:22.000000000 -0700 +@@ -54,36 +54,38 @@ + + #include "stun_client_ctx.h" + #include "stun_server_ctx.h" + #include "turn_client_ctx.h" + #include "ice_ctx.h" + #include "ice_candidate.h" + #include "ice_reg.h" + #include "ice_util.h" + #include "nr_socket_turn.h" + ++static int next_automatic_preference = 224; ++ + static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand); + static int nr_ice_srvrflx_start_stun(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); +-static void nr_ice_srvrflx_stun_finished_cb(int sock, int how, void *cb_arg); ++static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg); + #ifdef USE_TURN + static int nr_ice_start_relay_turn(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); +-static void nr_ice_turn_allocated_cb(int sock, int how, void *cb_arg); ++static void nr_ice_turn_allocated_cb(NR_SOCKET sock, int how, void *cb_arg); + #endif /* USE_TURN */ + + char *nr_ice_candidate_type_names[]={0,"host","srflx","prflx","relay",0}; + + int nr_ice_candidate_create(nr_ice_ctx *ctx,char *label,nr_ice_component *comp,nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp) + { + nr_ice_candidate *cand=0; + nr_ice_candidate *tmp=0; + int r,_status; +- ++ + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + if(!(cand->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + cand->state=NR_ICE_CAND_STATE_CREATED; + cand->ctx=ctx; + cand->isock=isock; + cand->osock=osock; + cand->type=ctype; + cand->stun_server=stun_server; +@@ -189,21 +191,21 @@ + if(cand->delay_timer) + NR_async_timer_cancel(cand->delay_timer); + + RFREE(cand->foundation); + RFREE(cand->label); + RFREE(cand); + + return(0); + } + +-void nr_ice_candidate_destroy_cb(int s, int h, void *cb_arg) ++void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + nr_ice_candidate_destroy(&cand); + } + + /* This algorithm is not super-fast, but I don't think we need a hash + table just yet and it produces a small foundation string */ + static int nr_ice_get_foundation(nr_ice_ctx *ctx,nr_ice_candidate *cand) + { + nr_ice_foundation *foundation; +@@ -276,22 +278,38 @@ + break; + default: + ABORT(R_INTERNAL); + } + + if(type_preference > 126) + r_log(LOG_ICE,LOG_ERR,"Illegal type preference %d",type_preference); + + + if(r=NR_reg_get2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname, +- &interface_preference)) +- ABORT(r); ++ &interface_preference)) { ++ if (r==R_NOT_FOUND) { ++ if (next_automatic_preference == 1) { ++ r_log(LOG_ICE,LOG_DEBUG,"Out of preference values. Can't assign one for interface %s",cand->base.ifname); ++ ABORT(R_NOT_FOUND); ++ } ++ r_log(LOG_ICE,LOG_DEBUG,"Automatically assigning preference for interface %s->%d",cand->base.ifname, ++ next_automatic_preference); ++ if (r=NR_reg_set2_uchar(NR_ICE_REG_PREF_INTERFACE_PRFX,cand->base.ifname,next_automatic_preference)){ ++ ABORT(r); ++ } ++ interface_preference=next_automatic_preference; ++ next_automatic_preference--; ++ } ++ else { ++ ABORT(r); ++ } ++ } + + cand->priority= + (type_preference << 24) | + (interface_preference << 16) | + (stun_priority << 8) | + (256 - cand->component_id); + + /* S 4.1.2 */ + assert(cand->priority>=1&&cand->priority<=2147483647); + +@@ -306,21 +324,22 @@ + + cand->done_cb=ready_cb; + cand->cb_arg=cb_arg; + + switch(cand->type){ + case HOST: + if(r=nr_socket_getaddr(cand->isock->sock,&cand->addr)) + ABORT(r); + cand->osock=cand->isock->sock; + cand->state=NR_ICE_CAND_STATE_INITIALIZED; +- ready_cb(0,0,cb_arg); ++ // Post this so that it doesn't happen in-line ++ NR_ASYNC_SCHEDULE(ready_cb,cb_arg); + break; + #ifdef USE_TURN + case RELAYED: + if(r=nr_ice_start_relay_turn(cand,ready_cb,cb_arg)) + ABORT(r); + ABORT(R_WOULDBLOCK); + break; + #endif /* USE_TURN */ + case SERVER_REFLEXIVE: + /* Need to start stun */ +@@ -333,21 +352,21 @@ + ABORT(R_INTERNAL); + } + + _status=0; + abort: + if(_status && _status!=R_WOULDBLOCK) + cand->state=NR_ICE_CAND_STATE_FAILED; + return(_status); + } + +-static void nr_ice_srvrflx_start_stun_timer_cb(int s, int how, void *cb_arg) ++static void nr_ice_srvrflx_start_stun_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + + cand->delay_timer=0; + + /* TODO: if the response is a BINDING-ERROR-RESPONSE, then restart + * TODO: using NR_STUN_CLIENT_MODE_BINDING_REQUEST because the + * TODO: server may not have understood the 0.96-style request */ + if(r=nr_stun_client_start(cand->u.srvrflx.stun, NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96, nr_ice_srvrflx_stun_finished_cb, cand)) +@@ -387,21 +406,21 @@ + + _status=0; + abort: + if(_status){ + cand->state=NR_ICE_CAND_STATE_FAILED; + } + return(_status); + } + + #ifdef USE_TURN +-static void nr_ice_start_relay_turn_timer_cb(int s, int how, void *cb_arg) ++static void nr_ice_start_relay_turn_timer_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_candidate *cand=cb_arg; + int r,_status; + int i; + + cand->delay_timer=0; + + if(r=nr_turn_client_allocate(cand->u.relayed.turn, cand->u.relayed.server->username, cand->u.relayed.server->password, cand->u.relayed.server->bandwidth_kbps, cand->u.relayed.server->lifetime_secs, nr_ice_turn_allocated_cb, cand)) + ABORT(r); + +@@ -443,21 +462,21 @@ + + _status=0; + abort: + if(_status){ + cand->state=NR_ICE_CAND_STATE_FAILED; + } + return(_status); + } + #endif /* USE_TURN */ + +-static void nr_ice_srvrflx_stun_finished_cb(int sock, int how, void *cb_arg) ++static void nr_ice_srvrflx_stun_finished_cb(NR_SOCKET sock, int how, void *cb_arg) + { + int _status; + nr_ice_candidate *cand=cb_arg; + + /* Deregister to suppress duplicates */ + if(cand->u.srvrflx.stun_handle){ /* This test because we might have failed before CB registered */ + nr_ice_socket_deregister(cand->isock,cand->u.srvrflx.stun_handle); + cand->u.srvrflx.stun_handle=0; + } + +@@ -481,40 +500,40 @@ + } + _status = 0; + abort: + if(_status){ + cand->state=NR_ICE_CAND_STATE_FAILED; + cand->done_cb(0,0,cand->cb_arg); + } + } + + #ifdef USE_TURN +-static void nr_ice_turn_allocated_cb(int s, int how, void *cb_arg) ++static void nr_ice_turn_allocated_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_candidate *cand=cb_arg; + nr_turn_client_ctx *turn=cand->u.relayed.turn; + int i; + char *label; + + /* Deregister to suppress duplicates */ + if(cand->u.relayed.turn_handle){ /* This test because we might have failed before CB registered */ + nr_ice_socket_deregister(cand->isock,cand->u.relayed.turn_handle); + cand->u.relayed.turn_handle=0; + } + + switch(turn->state){ + /* OK, we should have a mapped address */ + case NR_TURN_CLIENT_STATE_ALLOCATED: + /* switch candidate from TURN mode to STUN mode */ + +- if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",turn->relay_addr.as_string,")",0)) ++ if(r=nr_concat_strings(&label,"turn-relay(",cand->base.as_string,"|",turn->relay_addr.as_string,")",NULL)) + ABORT(r); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Switching from TURN (%s) to RELAY (%s)",cand->u.relayed.turn->label,cand->label,label); + + /* Copy out mapped address and relay address */ + nr_transport_addr_copy(&turn->relay_addr, &cand->u.relayed.turn->stun_ctx[NR_TURN_CLIENT_PHASE_ALLOCATE_REQUEST2]->results.allocate_response2.relay_addr); + nr_transport_addr_copy(&cand->addr, &turn->relay_addr); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-CANDIDATE(%s): base=%s, candidate=%s", cand->label, cand->base.as_string, cand->addr.as_string); + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.h src/ice/ice_candidate.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate.h 2012-10-06 08:30:22.000000000 -0700 +@@ -41,21 +41,22 @@ + + typedef enum {HOST=1, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED} nr_ice_candidate_type; + + struct nr_ice_candidate_ { + char *label; + int state; + #define NR_ICE_CAND_STATE_CREATED 1 + #define NR_ICE_CAND_STATE_INITIALIZING 2 + #define NR_ICE_CAND_STATE_INITIALIZED 3 + #define NR_ICE_CAND_STATE_FAILED 4 +-#define NR_ICE_CAND_PEER_CANDIDATE 10 ++#define NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED 9 ++#define NR_ICE_CAND_PEER_CANDIDATE_PAIRED 10 + struct nr_ice_ctx_ *ctx; + nr_ice_socket *isock; /* The socket to read from + (it contains all other candidates + on this socket) */ + nr_socket *osock; /* The socket to write to */ + nr_ice_media_stream *stream; /* The media stream this is associated with */ + nr_ice_component *component; /* The component this is associated with */ + nr_ice_candidate_type type; /* The type of the candidate (S 4.1.1) */ + UCHAR component_id; /* The component id (S 4.1.2.1) */ + nr_transport_addr addr; /* The advertised address; +@@ -89,21 +90,21 @@ + TAILQ_ENTRY(nr_ice_candidate_) entry_comp; + }; + + extern char *nr_ice_candidate_type_names[]; + + + int nr_ice_candidate_create(struct nr_ice_ctx_ *ctx,char *label, nr_ice_component *component, nr_ice_socket *isock, nr_socket *osock, nr_ice_candidate_type ctype, nr_ice_stun_server *stun_server, UCHAR component_id, nr_ice_candidate **candp); + int nr_ice_candidate_initialize(nr_ice_candidate *cand, NR_async_cb ready_cb, void *cb_arg); + int nr_ice_candidate_process_stun(nr_ice_candidate *cand, UCHAR *msg, int len, nr_transport_addr *faddr); + int nr_ice_candidate_destroy(nr_ice_candidate **candp); +-void nr_ice_candidate_destroy_cb(int s, int h, void *cb_arg); ++void nr_ice_candidate_destroy_cb(NR_SOCKET s, int h, void *cb_arg); + int nr_ice_format_candidate_attribute(nr_ice_candidate *cand, char *attr, int maxlen); + int nr_ice_peer_candidate_from_attribute(nr_ice_ctx *ctx,char *attr,nr_ice_media_stream *stream,nr_ice_candidate **candp); + int nr_ice_peer_peer_rflx_candidate_create(nr_ice_ctx *ctx,char *label, nr_ice_component *comp,nr_transport_addr *addr, nr_ice_candidate **candp); + int nr_ice_candidate_compute_priority(nr_ice_candidate *cand); + + #ifdef __cplusplus + } + #endif /* __cplusplus */ + #endif + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.c src/ice/ice_candidate_pair.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate_pair.c 2012-10-06 08:30:22.000000000 -0700 +@@ -37,21 +37,21 @@ + #include <assert.h> + #include <string.h> + #include <nr_api.h> + #include "ice_ctx.h" + #include "ice_util.h" + #include "ice_codeword.h" + #include "stun.h" + + static char *nr_ice_cand_pair_states[]={"UNKNOWN","FROZEN","WAITING","IN_PROGRESS","FAILED","SUCCEEDED","CANCELLED"}; + +-static void nr_ice_candidate_pair_restart_stun_controlled_cb(int s, int how, void *cb_arg); ++static void nr_ice_candidate_pair_restart_stun_controlled_cb(NR_SOCKET s, int how, void *cb_arg); + static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand); + + int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp) + { + nr_ice_cand_pair *pair=0; + UINT8 o_priority, a_priority; + char *lufrag,*rufrag; + char *lpwd,*rpwd; + char *l2ruser=0,*r2lpass=0; +@@ -61,21 +61,21 @@ + UINT8 t_priority; + + if(!(pair=RCALLOC(sizeof(nr_ice_cand_pair)))) + ABORT(R_NO_MEMORY); + + pair->pctx=pctx; + + nr_ice_candidate_pair_compute_codeword(pair,lcand,rcand); + + if(r=nr_concat_strings(&pair->as_string,pair->codeword,"|",lcand->addr.as_string,"|", +- rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",0)) ++ rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")", NULL)) + ABORT(r); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + pair->local=lcand; + pair->remote=rcand; + + /* Priority computation S 5.7.2 */ + if(pctx->ctx->flags & NR_ICE_CTX_FLAGS_OFFERER) + { + assert(!(pctx->ctx->flags & NR_ICE_CTX_FLAGS_ANSWERER)); +@@ -87,21 +87,21 @@ + o_priority=rcand->priority; + a_priority=lcand->priority; + } + pair->priority=(MIN(o_priority, a_priority))<<32 | + (MAX(o_priority, a_priority))<<1 | (o_priority > a_priority?0:1); + + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Pairing candidate %s (%x):%s (%x) priority=%llu (%llx) codeword=%s",pctx->ctx->label,lcand->addr.as_string,lcand->priority,rcand->addr.as_string,rcand->priority,pair->priority,pair->priority,pair->codeword); + + /* Foundation */ + if(r=nr_concat_strings(&pair->foundation,lcand->foundation,"|", +- rcand->foundation,0)) ++ rcand->foundation,NULL)) + ABORT(r); + + + /* OK, now the STUN data */ + lufrag=lcand->stream->ufrag?lcand->stream->ufrag:pctx->ctx->ufrag; + lpwd=lcand->stream->pwd?lcand->stream->pwd:pctx->ctx->pwd; + rufrag=rcand->stream->ufrag?rcand->stream->ufrag:pctx->peer_ufrag; + rpwd=rcand->stream->pwd?rcand->stream->pwd:pctx->peer_pwd; + + +@@ -110,39 +110,39 @@ + + /* Make a bogus candidate to compute a theoretical peer reflexive + * priority per S 7.1.1.1 */ + memcpy(&tmpcand, lcand, sizeof(tmpcand)); + tmpcand.type = PEER_REFLEXIVE; + if (r=nr_ice_candidate_compute_priority(&tmpcand)) + ABORT(r); + t_priority = tmpcand.priority; + + /* Our sending context */ +- if(r=nr_concat_strings(&l2ruser,lufrag,":",rufrag,0)) ++ if(r=nr_concat_strings(&l2ruser,lufrag,":",rufrag,NULL)) + ABORT(r); + if(r=nr_stun_client_ctx_create(pair->as_string, + lcand->osock, + &rcand->addr,RTO,&pair->stun_client)) + ABORT(r); + if(!(pair->stun_client->params.ice_binding_request.username=r_strdup(l2ruser))) + ABORT(R_NO_MEMORY); + if(r=r_data_make(&pair->stun_client->params.ice_binding_request.password,(UCHAR *)lpwd,strlen(lpwd))) + ABORT(r); + pair->stun_client->params.ice_binding_request.priority=t_priority; + pair->stun_client->params.ice_binding_request.control = pctx->controlling? + NR_ICE_CONTROLLING:NR_ICE_CONTROLLED; + + pair->stun_client->params.ice_binding_request.tiebreaker=pctx->tiebreaker; + + /* Our receiving username/passwords. Stash these for later + injection into the stun server ctx*/ +- if(r=nr_concat_strings(&pair->r2l_user,rufrag,":",lufrag,0)) ++ if(r=nr_concat_strings(&pair->r2l_user,rufrag,":",lufrag,NULL)) + ABORT(r); + if(!(r2lpass=r_strdup(rpwd))) + ABORT(R_NO_MEMORY); + INIT_DATA(pair->r2l_pwd,(UCHAR *)r2lpass,strlen(r2lpass)); + + *pairp=pair; + + _status=0; + abort: + RFREE(l2ruser); +@@ -178,21 +178,21 @@ + + int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair) + { + assert(pair->state==NR_ICE_PAIR_STATE_FROZEN); + + nr_ice_candidate_pair_set_state(pctx,pair,NR_ICE_PAIR_STATE_WAITING); + + return(0); + } + +-static void nr_ice_candidate_pair_stun_cb(int s, int how, void *cb_arg) ++static void nr_ice_candidate_pair_stun_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_ice_cand_pair *pair=cb_arg,*orig_pair; + nr_ice_candidate *cand=0; + nr_stun_message *sres; + nr_transport_addr *request_src; + nr_transport_addr *request_dst; + nr_transport_addr *response_src; + nr_transport_addr response_dst; + nr_stun_message_attribute *attr; +@@ -457,32 +457,47 @@ + abort: + return(_status); + } + + int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state) + { + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): setting pair %s to %s", + pctx->label,pair->as_string,nr_ice_cand_pair_states[state]); +- pair->state=state; + +- if(pctx->state!=NR_ICE_PAIR_STATE_WAITING){ ++ /* NOTE: This function used to reference pctx->state instead of ++ pair->state and the assignment to pair->state was at the top ++ of this function. Because pctx->state was never changed, this seems to have ++ been a typo. The natural logic is "if the state changed ++ decrement the counter" so this implies we should be checking ++ the pair state rather than the pctx->state. ++ ++ This didn't cause big problems because waiting_pairs was only ++ used for pacing, so the pacing just was kind of broken. ++ ++ This note is here as a reminder until we do more testing ++ and make sure that in fact this was a typo. ++ */ ++ if(pair->state!=NR_ICE_PAIR_STATE_WAITING){ + if(state==NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs++; + } + else{ + if(state!=NR_ICE_PAIR_STATE_WAITING) + pctx->waiting_pairs--; + + assert(pctx->waiting_pairs>=0); + } ++ pair->state=state; ++ ++ + if(pair->state==NR_ICE_PAIR_STATE_FAILED){ + if(r=nr_ice_component_failed_pair(pair->remote->component, pair)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +@@ -505,42 +520,42 @@ + break; + } + + c1=TAILQ_NEXT(c1,entry); + } + if(!c1) TAILQ_INSERT_TAIL(head,pair,entry); + + return(0); + } + +-void nr_ice_candidate_pair_restart_stun_nominated_cb(int s, int how, void *cb_arg) ++void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as nominated",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string); + + nr_stun_client_reset(pair->stun_client); + pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLING; + + if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_USE_CANDIDATE,nr_ice_candidate_pair_stun_cb,pair)) + ABORT(r); + + if(r=nr_ice_ctx_remember_id(pair->pctx->ctx, pair->stun_client->request)) + ABORT(r); + + _status=0; + abort: + return; + } + +-static void nr_ice_candidate_pair_restart_stun_controlled_cb(int s, int how, void *cb_arg) ++static void nr_ice_candidate_pair_restart_stun_controlled_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_cand_pair *pair=cb_arg; + int r,_status; + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s):%d: Restarting pair %s as CONTROLLED",pair->pctx->label,pair->local->stream->label,pair->remote->component->component_id,pair->as_string); + + nr_stun_client_reset(pair->stun_client); + pair->stun_client->params.ice_binding_request.control=NR_ICE_CONTROLLED; + + if(r=nr_stun_client_start(pair->stun_client,NR_ICE_CLIENT_MODE_BINDING_REQUEST,nr_ice_candidate_pair_stun_cb,pair)) +@@ -556,21 +571,21 @@ + + + + static void nr_ice_candidate_pair_compute_codeword(nr_ice_cand_pair *pair, + nr_ice_candidate *lcand, nr_ice_candidate *rcand) + { + int r,_status; + char *as_string=0; + + if(r=nr_concat_strings(&as_string,lcand->addr.as_string,"|", +- rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",0)) ++ rcand->addr.as_string,"(",lcand->label,"|",rcand->label,")",NULL)) + ABORT(r); + + nr_ice_compute_codeword(as_string,strlen(as_string),pair->codeword); + + _status=0; + abort: + RFREE(as_string); + return; + } + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.h src/ice/ice_candidate_pair.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_candidate_pair.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_candidate_pair.h 2012-10-06 08:30:22.000000000 -0700 +@@ -72,18 +72,18 @@ + + int nr_ice_candidate_pair_create(nr_ice_peer_ctx *pctx, nr_ice_candidate *lcand,nr_ice_candidate *rcand,nr_ice_cand_pair **pairp); + int nr_ice_candidate_pair_unfreeze(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_start(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_set_state(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair, int state); + int nr_ice_candidate_pair_dump_state(nr_ice_cand_pair *pair, FILE *out); + int nr_ice_candidate_pair_cancel(nr_ice_peer_ctx *pctx,nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_select(nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_do_triggered_check(nr_ice_peer_ctx *pctx, nr_ice_cand_pair *pair); + int nr_ice_candidate_pair_insert(nr_ice_cand_pair_head *head,nr_ice_cand_pair *pair); +-void nr_ice_candidate_pair_restart_stun_nominated_cb(int s, int how, void *cb_arg); ++void nr_ice_candidate_pair_restart_stun_nominated_cb(NR_SOCKET s, int how, void *cb_arg); + int nr_ice_candidate_pair_destroy(nr_ice_cand_pair **pairp); + + #ifdef __cplusplus + } + #endif /* __cplusplus */ + #endif + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_component.c src/ice/ice_component.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_component.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_component.c 2012-10-06 08:30:22.000000000 -0700 +@@ -451,21 +451,21 @@ + if(r=nr_ice_peer_peer_rflx_candidate_create(comp->stream->pctx->ctx,"prflx",comp,&req->src_addr,&pcand)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + if(!nr_stun_message_has_attribute(sreq,NR_STUN_ATTR_PRIORITY,&attr)){ + r_log(LOG_ICE,LOG_ERR,"ICE-PEER(%s): Rejecting stun request without priority",comp->stream->pctx->label); + *error=487; + ABORT(R_BAD_DATA); + } + pcand->priority=attr->u.priority; +- pcand->state=NR_ICE_CAND_PEER_CANDIDATE; ++ pcand->state=NR_ICE_CAND_PEER_CANDIDATE_PAIRED;; + TAILQ_INSERT_TAIL(&comp->candidates,pcand,entry_comp); + + if(r=nr_ice_candidate_pair_create(comp->stream->pctx,cand,pcand, + &pair)) { + *error=(r==R_NO_MEMORY)?500:400; + ABORT(r); + } + nr_ice_candidate_pair_set_state(pair->pctx,pair,NR_ICE_PAIR_STATE_FROZEN); + + if(r=nr_ice_candidate_pair_insert(&comp->stream->check_list,pair)) { +@@ -563,30 +563,38 @@ + break; + } + + /* PAIR with each peer*/ + if(TAILQ_EMPTY(&pcomp->candidates)) { + /* can happen if our peer proposes no (or all bogus) candidates */ + goto next_cand; + } + pcand=TAILQ_FIRST(&pcomp->candidates); + while(pcand){ +- nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); +- r_log(LOG_ICE,LOG_DEBUG,"Examining peer candidate %s:%s",codeword,pcand->label); +- +- if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) +- ABORT(r); +- +- if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list, +- pair)) +- ABORT(r); ++ /* Only pair peer candidates which have not yet been paired. ++ This allows "trickle ICE". (Not yet standardized, but ++ part of WebRTC). ++ ++ TODO(ekr@rtfm.com): Add refernece to the spec when there ++ is one. ++ */ ++ if (pcand->state = NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED) { ++ nr_ice_compute_codeword(pcand->label,strlen(pcand->label),codeword); ++ r_log(LOG_ICE,LOG_DEBUG,"Examining peer candidate %s:%s",codeword,pcand->label); ++ ++ if(r=nr_ice_candidate_pair_create(pctx,lcand,pcand,&pair)) ++ ABORT(r); + ++ if(r=nr_ice_candidate_pair_insert(&pcomp->stream->check_list, ++ pair)) ++ ABORT(r); ++ } + pcand=TAILQ_NEXT(pcand,entry_comp); + } + + if(!pair) + ABORT(R_INTERNAL); + + /* Add the stun username/password pair from the last pair (any + would do) to the stun contexts */ + isock=STAILQ_FIRST(&lcomp->sockets); + while(isock){ +@@ -594,20 +602,28 @@ + pair->r2l_user,&pair->r2l_pwd,nr_ice_component_stun_server_cb,pcomp)) + ABORT(r); + + isock=STAILQ_NEXT(isock,entry); + } + + next_cand: + lcand=TAILQ_NEXT(lcand,entry_comp); + } + ++ /* Mark all peer candidates as paired */ ++ pcand=TAILQ_FIRST(&pcomp->candidates); ++ while(pcand){ ++ pcand->state = NR_ICE_CAND_PEER_CANDIDATE_PAIRED; ++ ++ pcand=TAILQ_NEXT(pcand,entry_comp); ++ } ++ + _status=0; + abort: + return(_status); + } + + int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair) + { + int r,_status; + int fire_cb=0; + nr_ice_cand_pair *p2; +@@ -616,32 +632,32 @@ + fire_cb=1; + + /* Are we changing what the nominated pair is? */ + if(comp->nominated){ + if(comp->nominated->priority > pair->priority) + return(0); + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): replacing pair %s with pair %s",comp->stream->pctx->label,comp->stream->label,comp->component_id,comp->nominated->as_string,pair->as_string); + } + + /* Set the new nominated pair */ +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): nominated pair is %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,(int)pair); ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): nominated pair is %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair); + comp->state=NR_ICE_COMPONENT_NOMINATED; + comp->nominated=pair; + comp->active=pair; + +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling all pairs but %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,(int)pair); ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling all pairs but %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,pair->as_string,pair); + + /* OK, we need to cancel off everything on this component */ + p2=TAILQ_FIRST(&comp->stream->check_list); + while(p2){ + if((p2 != pair) && (p2->remote->component->component_id == comp->component_id)){ +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling pair %s (0x%x)",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->as_string,(int)p2); ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling pair %s (0x%p)",comp->stream->pctx->label,comp->stream->label,comp->component_id,p2->as_string,p2); + + if(r=nr_ice_candidate_pair_cancel(pair->pctx,p2)) + ABORT(r); + } + + p2=TAILQ_NEXT(p2,entry); + } + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s)/STREAM(%s)/comp(%d): cancelling done",comp->stream->pctx->label,comp->stream->label,comp->component_id); + + if(r=nr_ice_media_stream_component_nominated(comp->stream,comp)) +@@ -734,21 +750,21 @@ + ABORT(r); + } + + _status=0; + abort: + RFREE(pairs); + return(_status); + } + + +-static void nr_ice_component_keepalive_cb(int s, int how, void *cb_arg) ++static void nr_ice_component_keepalive_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_component *comp=cb_arg; + UINT4 keepalive_timeout; + + assert(comp->keepalive_ctx); + + if(NR_reg_get_uint4(NR_ICE_REG_KEEPALIVE_TIMER,&keepalive_timeout)){ + keepalive_timeout=15000; /* Default */ + } + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.c src/ice/ice_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -56,21 +56,21 @@ + #include "util.h" + + + int LOG_ICE = 0; + + static int nr_ice_random_string(char *str, int len); + static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out); + #ifdef USE_TURN + static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out); + #endif /* USE_TURN */ +-static void nr_ice_ctx_destroy_cb(int s, int how, void *cb_arg); ++static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg); + + int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out) + { + int r,_status; + nr_ice_stun_server *servers = 0; + int i; + NR_registry child; + char *addr=0; + UINT2 port; + in_addr_t addr_int; +@@ -271,21 +271,21 @@ + *ctxp=ctx; + + _status=0; + abort: + if(_status) + nr_ice_ctx_destroy_cb(0,0,ctx); + + return(_status); + } + +-static void nr_ice_ctx_destroy_cb(int s, int how, void *cb_arg) ++static void nr_ice_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_ctx *ctx=cb_arg; + nr_ice_foundation *f1,*f2; + nr_ice_media_stream *s1,*s2; + int i; + nr_ice_stun_id *id1,*id2; + + RFREE(ctx->label); + + RFREE(ctx->stun_servers); +@@ -323,21 +323,21 @@ + if(!ctxp || !*ctxp) + return(0); + + NR_ASYNC_SCHEDULE(nr_ice_ctx_destroy_cb,*ctxp); + + *ctxp=0; + + return(0); + } + +-void nr_ice_initialize_finished_cb(int s, int h, void *cb_arg) ++void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg) + { + nr_ice_ctx *ctx=cb_arg; + + /* r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Candidate %s %s",ctx->label, + cand->label, cand->state==NR_ICE_CAND_STATE_INITIALIZED?"INITIALIZED":"FAILED"); + */ + ctx->uninitialized_candidates--; + + if(ctx->uninitialized_candidates==0){ + r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): All candidates initialized",ctx->label); +@@ -368,21 +368,22 @@ + stream=STAILQ_FIRST(&ctx->streams); + while(stream){ + if(r=nr_ice_media_stream_initialize(ctx,stream)) + ABORT(r); + + stream=STAILQ_NEXT(stream,entry); + } + + if(ctx->uninitialized_candidates) + ABORT(R_WOULDBLOCK); +- ++ ++ + _status=0; + abort: + return(_status); + } + + int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp) + { + int r,_status; + + if(r=nr_ice_media_stream_create(ctx,label,components,streamp)) +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.h src/ice/ice_ctx.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_ctx.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_ctx.h 2012-10-06 08:30:22.000000000 -0700 +@@ -92,23 +92,23 @@ + UCHAR id[12]; + + STAILQ_ENTRY(nr_ice_stun_id_) entry; + } nr_ice_stun_id; + + typedef STAILQ_HEAD(nr_ice_stun_id_head_,nr_ice_stun_id_) nr_ice_stun_id_head; + + struct nr_ice_ctx_ { + UINT4 flags; + int state; +-#define NR_ICE_STATE_CREATED 1 +-#define NR_ICE_STATE_INITIALIZING 2 +-#define NR_ICE_STATE_INITIALIZED 3 ++#define NR_ICE_STATE_CREATED 1 ++#define NR_ICE_STATE_INITIALIZING 2 ++#define NR_ICE_STATE_INITIALIZED 3 + char *label; + + char *ufrag; + char *pwd; + + UINT4 Ta; + + nr_ice_stun_server *stun_servers; /* The list of stun servers */ + int stun_server_ct; + nr_ice_turn_server *turn_servers; /* The list of turn servers */ +@@ -133,21 +133,21 @@ + + int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp); + #define NR_ICE_CTX_FLAGS_OFFERER 1 + #define NR_ICE_CTX_FLAGS_ANSWERER (1<<1) + #define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1<<2) + #define NR_ICE_CTX_FLAGS_LITE (1<<3) + + int nr_ice_ctx_destroy(nr_ice_ctx **ctxp); + int nr_ice_initialize(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg); + int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand); +-void nr_ice_initialize_finished_cb(int s, int h, void *cb_arg); ++void nr_ice_initialize_finished_cb(NR_SOCKET s, int h, void *cb_arg); + int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp); + int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp); + int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); + int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]); + int nr_ice_ctx_remember_id(nr_ice_ctx *ctx, nr_stun_message *msg); + int nr_ice_ctx_finalize(nr_ice_ctx *ctx, nr_ice_peer_ctx *pctx); + + extern int LOG_ICE; + + #ifdef __cplusplus +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.c src/ice/ice_media_stream.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_media_stream.c 2012-10-06 08:30:22.000000000 -0700 +@@ -35,21 +35,21 @@ + static char *RCSSTRING __UNUSED__="$Id: ice_media_stream.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + + #include <string.h> + #include <assert.h> + #include <nr_api.h> + #include <r_assoc.h> + #include <async_timer.h> + #include "ice_ctx.h" + + static char *nr_ice_media_stream_states[]={"INVALID", +- "FROZEN","ACTIVE","COMPLETED","FAILED" ++ "UNPAIRED","FROZEN","ACTIVE","COMPLETED","FAILED" + }; + + int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); + + int nr_ice_media_stream_create(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp) + { + int r,_status; + nr_ice_media_stream *stream=0; + nr_ice_component *comp=0; + int i; +@@ -66,29 +66,29 @@ + for(i=0;i<components;i++){ + /* component-id must be > 0, so increment by 1 */ + if(r=nr_ice_component_create(stream, i+1, &comp)) + ABORT(r); + + } + + TAILQ_INIT(&stream->check_list); + + stream->component_ct=components; +- ++ stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED; + *streamp=stream; + + _status=0; + abort: + if(_status){ + nr_ice_media_stream_destroy(&stream); + } +- return(_status); ++ return(_status); + } + + int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp) + { + nr_ice_media_stream *stream; + nr_ice_component *c1,*c2; + nr_ice_cand_pair *p1,*p2; + if(!streamp || !*streamp) + return(0); + +@@ -200,85 +200,148 @@ + if(attrs){ + for(index=0;index<attrct;index++){ + RFREE(attrs[index]); + } + RFREE(attrs); + } + } + return(_status); + } + ++ ++/* Get a default candidate per 4.1.4 */ ++int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp) ++ { ++ int _status; ++ nr_ice_component *comp; ++ nr_ice_candidate *cand; ++ nr_ice_candidate *best_cand = NULL; ++ ++ comp=STAILQ_FIRST(&stream->components); ++ while(comp){ ++ if (comp->component_id == component) ++ break; ++ ++ comp=STAILQ_NEXT(comp,entry); ++ } ++ ++ if (!comp) ++ ABORT(R_NOT_FOUND); ++ ++ /* We have the component. Now find the "best" candidate, making ++ use of the fact that more "reliable" candidate types have ++ higher numbers. So, we sort by type and then priority within ++ type ++ */ ++ cand=TAILQ_FIRST(&comp->candidates); ++ while(cand){ ++ if (!best_cand) { ++ best_cand = cand; ++ } ++ else { ++ if (best_cand->type < cand->type) { ++ best_cand = cand; ++ } else if (best_cand->type == cand->type) { ++ if (best_cand->priority < cand->priority) ++ best_cand = cand; ++ } ++ } ++ ++ cand=TAILQ_NEXT(cand,entry_comp); ++ } ++ ++ /* No candidates */ ++ if (!best_cand) ++ ABORT(R_NOT_FOUND); ++ ++ *candp = best_cand; ++ ++ _status=0; ++ abort: ++ return(_status); ++ } ++ ++ + int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream) + { + int r,_status; + nr_ice_component *pcomp,*lcomp; + + pcomp=STAILQ_FIRST(&pstream->components); + lcomp=STAILQ_FIRST(&lstream->components); + while(pcomp){ + if(r=nr_ice_component_pair_candidates(pctx,lcomp,pcomp)) + ABORT(r); +- ++ + lcomp=STAILQ_NEXT(lcomp,entry); + pcomp=STAILQ_NEXT(pcomp,entry); + }; + ++ if (pstream->ice_state == NR_ICE_MEDIA_STREAM_UNPAIRED) { ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): unfreezing stream %s",pstream->pctx->label,pstream->label); ++ pstream->ice_state = NR_ICE_MEDIA_STREAM_CHECKS_FROZEN; ++ } ++ + _status=0; + abort: + return(_status); + } + + /* S 5.8 -- run the highest priority WAITING pair or if not available + FROZEN pair */ +-static void nr_ice_media_stream_check_timer_cb(int s, int h, void *cb_arg) ++static void nr_ice_media_stream_check_timer_cb(NR_SOCKET s, int h, void *cb_arg) + { + int r,_status; + nr_ice_media_stream *stream=cb_arg; + nr_ice_cand_pair *pair; + int timer_val; + + assert(stream->pctx->active_streams!=0); + + timer_val=stream->pctx->ctx->Ta*stream->pctx->active_streams; + ++ if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) { ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): bogus state for stream %s",stream->pctx->label,stream->label); ++ } + assert(stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED); + + r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): check timer expired for media stream %s",stream->pctx->label,stream->label); + stream->timer=0; + +- + /* Find the highest priority WAITING check and move it to RUNNING */ + pair=TAILQ_FIRST(&stream->check_list); + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_WAITING) + break; + pair=TAILQ_NEXT(pair,entry); + } + + /* Hmmm... No WAITING. Let's look for FROZEN */ + if(!pair){ + pair=TAILQ_FIRST(&stream->check_list); +- ++ + while(pair){ + if(pair->state==NR_ICE_PAIR_STATE_FROZEN){ + if(r=nr_ice_candidate_pair_unfreeze(stream->pctx,pair)) + ABORT(r); + break; + } + pair=TAILQ_NEXT(pair,entry); + } + } + + if(pair){ + nr_ice_candidate_pair_start(pair->pctx,pair); /* Ignore failures */ + NR_ASYNC_TIMER_SET(timer_val,nr_ice_media_stream_check_timer_cb,cb_arg,&stream->timer); + } ++ /* TODO(ekr@rtfm.com): Report on the special case where there are no checks to ++ run at all */ + _status=0; + abort: + return; + } + + + /* Start checks for this media stream (aka check list) */ + int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + assert(stream->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FROZEN); +@@ -476,21 +539,23 @@ + /* All done... */ + r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all components have nominated candidate pairs",stream->pctx->label,stream->label); + nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED); + + /* Cancel our timer */ + if(stream->timer){ + NR_async_timer_cancel(stream->timer); + stream->timer=0; + } + +- stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream); ++ if (stream->pctx->handler) { ++ stream->pctx->handler->vtbl->stream_ready(stream->pctx->handler->obj,stream->local_stream); ++ } + + /* Now tell the peer_ctx that we're done */ + if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream)) + ABORT(r); + + done: + _status=0; + abort: + return(_status); + } +@@ -515,21 +580,23 @@ + + p2=TAILQ_NEXT(p2,entry); + } + + /* Cancel our timer */ + if(stream->timer){ + NR_async_timer_cancel(stream->timer); + stream->timer=0; + } + +- stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream); ++ if (stream->pctx->handler) { ++ stream->pctx->handler->vtbl->stream_failed(stream->pctx->handler->obj,stream->local_stream); ++ } + + /* Now tell the peer_ctx that we're done */ + if(r=nr_ice_peer_ctx_stream_done(stream->pctx,stream)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.h src/ice/ice_media_stream.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_media_stream.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_media_stream.h 2012-10-06 08:30:22.000000000 -0700 +@@ -45,40 +45,43 @@ + struct nr_ice_peer_ctx_ *pctx; + + struct nr_ice_media_stream_ *local_stream; /* used when this is a peer */ + int component_ct; + nr_ice_component_head components; + + char *ufrag; /* ICE username */ + char *pwd; /* ICE password */ + + int ice_state; +-#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 1 +-#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 2 +-#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 3 +-#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 4 ++ ++#define NR_ICE_MEDIA_STREAM_UNPAIRED 1 ++#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2 ++#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 3 ++#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 4 ++#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5 + + nr_ice_cand_pair_head check_list; + void *timer; /* Check list periodic timer */ + + // nr_ice_cand_pair_head valid_list; +- ++ + STAILQ_ENTRY(nr_ice_media_stream_) entry; + }; + + typedef STAILQ_HEAD(nr_ice_media_stream_head_,nr_ice_media_stream_) nr_ice_media_stream_head; + + int nr_ice_media_stream_create(struct nr_ice_ctx_ *ctx,char *label, int components, nr_ice_media_stream **streamp); + int nr_ice_media_stream_destroy(nr_ice_media_stream **streamp); + int nr_ice_media_stream_finalize(nr_ice_media_stream *lstr,nr_ice_media_stream *rstr); + int nr_ice_media_stream_initialize(struct nr_ice_ctx_ *ctx, nr_ice_media_stream *stream); + int nr_ice_media_stream_get_attributes(nr_ice_media_stream *stream, char ***attrsp,int *attrctp); ++int nr_ice_media_stream_get_default_candidate(nr_ice_media_stream *stream, int component, nr_ice_candidate **candp); + int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream); + int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); + int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); + int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation); + int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out); + int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component); + int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component); + int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state); + int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp); + int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx,nr_ice_media_stream *str, int component, UCHAR *data, int len); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_parser.c src/ice/ice_parser.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_parser.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_parser.c 2012-10-06 08:30:22.000000000 -0700 +@@ -35,20 +35,21 @@ + static char *RCSSTRING __UNUSED__="$Id: ice_parser.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + + #include <csi_platform.h> + #include <sys/types.h> + #ifdef WIN32 + #include <winsock2.h> + #else + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> ++#include <strings.h> + #endif + #include <string.h> + #include <assert.h> + #include <ctype.h> + #include "nr_api.h" + #include "ice_ctx.h" + #include "ice_candidate.h" + #include "ice_reg.h" + + static void +@@ -125,21 +126,21 @@ + char *rel_addr=0; + + if(!(cand=RCALLOC(sizeof(nr_ice_candidate)))) + ABORT(R_NO_MEMORY); + + if(!(cand->label=r_strdup(orig))) + ABORT(R_NO_MEMORY); + + cand->ctx=ctx; + cand->isock=0; +- cand->state=NR_ICE_CAND_PEER_CANDIDATE; ++ cand->state=NR_ICE_CAND_PEER_CANDIDATE_UNPAIRED; + cand->stream=stream; + skip_whitespace(&str); + + /* Candidate attr */ + if (strncasecmp(str, "candidate:", 10)) + ABORT(R_BAD_DATA); + + fast_forward(&str, 10); + if (*str == '\0') + ABORT(R_BAD_DATA); +@@ -311,26 +312,31 @@ + /* it's expected to be at EOD at this point */ + + break; + default: + ABORT(R_INTERNAL); + break; + } + + skip_whitespace(&str); + +- assert(strlen(str) == 0); +- ++ /* This used to be an assert, but we don't want to exit on invalid ++ remote data */ ++ if (strlen(str) != 0) { ++ ABORT(R_BAD_DATA); ++ } ++ + *candp=cand; + + _status=0; + abort: ++ /* TODO(ekr@rtfm.com): Fix memory leak if we have a parse error */ + if (_status) + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): Error parsing attribute: %s",ctx->label,orig); + + RFREE(connection_address); + RFREE(rel_addr); + return(_status); + } + + + int +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.c src/ice/ice_peer_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_peer_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -35,33 +35,35 @@ + static char *RCSSTRING __UNUSED__="$Id: ice_peer_ctx.c,v 1.2 2008/04/28 17:59:01 ekr Exp $"; + + #include <string.h> + #include <assert.h> + #include <nr_api.h> + #include "ice_ctx.h" + #include "ice_peer_ctx.h" + #include "nr_crypto.h" + #include "async_timer.h" + +-static void nr_ice_peer_ctx_destroy_cb(int s, int how, void *cb_arg); ++static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg); ++static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct); ++static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate); + + int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp) + { + int r,_status; + nr_ice_peer_ctx *pctx=0; + + if(!(pctx=RCALLOC(sizeof(nr_ice_peer_ctx)))) + ABORT(R_NO_MEMORY); + + if(!(pctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); +- ++ + pctx->ctx=ctx; + pctx->handler=handler; + + /* Decide controlling vs. controlled */ + if(ctx->flags & NR_ICE_CTX_FLAGS_LITE){ + if(pctx->peer_lite){ + r_log(LOG_ICE,LOG_ERR,"Both sides are ICE-Lite"); + ABORT(R_BAD_DATA); + } + +@@ -88,85 +90,177 @@ + nr_ice_peer_ctx_destroy_cb(0,0,pctx); + } + return(_status); + } + + + + int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct) + { + nr_ice_media_stream *pstream=0; +- nr_ice_candidate *cand=0; + nr_ice_component *comp,*comp2; + int r,_status; +- int i,j; + +- /* Note: use component_ct from our own stream since components other +- than this offered by the other side are unusable */ ++ /* ++ Note: use component_ct from our own stream since components other ++ than this offered by the other side are unusable */ + if(r=nr_ice_media_stream_create(pctx->ctx,stream->label,stream->component_ct,&pstream)) + ABORT(r); +- +- /* Match up the local and remote components */ ++ ++ /* Match up the local and remote components */ + comp=STAILQ_FIRST(&stream->components); + comp2=STAILQ_FIRST(&pstream->components); + while(comp){ + comp2->local_component=comp; + + comp=STAILQ_NEXT(comp,entry); + comp2=STAILQ_NEXT(comp2,entry); + } +- + +- pstream->ice_state=NR_ICE_MEDIA_STREAM_CHECKS_FROZEN; + pstream->local_stream=stream; + pstream->pctx=pctx; + ++ if (r=nr_ice_peer_ctx_parse_stream_attributes_int(pctx,stream,pstream,attrs,attr_ct)) ++ ABORT(r); ++ ++ ++ STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry); ++ ++ _status=0; ++ abort: ++ return(_status); ++ } ++ ++static int nr_ice_peer_ctx_parse_stream_attributes_int(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream *pstream, char **attrs, int attr_ct) ++ { ++ int r; ++ int i; ++ + for(i=0;i<attr_ct;i++){ + if(!strncmp(attrs[i],"ice-",4)){ +- if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) ++ if(r=nr_ice_peer_ctx_parse_media_stream_attribute(pctx,pstream,attrs[i])) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus ICE attribute",pctx->ctx->label,pctx->label); + continue; +- continue; ++ } + } +- +- if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,attrs[i],pstream,&cand)) +- continue; +- if(cand->component_id-1>=pstream->component_ct){ +- r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label); +- continue; ++ else if (!strncmp(attrs[i],"candidate",9)){ ++ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,attrs[i])) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus candidate",pctx->ctx->label,pctx->label); ++ continue; ++ } ++ } ++ else { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified bogus attribute",pctx->ctx->label,pctx->label); + } ++ } + +- /* Not the fastest way to find a component, but it's what we got */ +- j=1; +- for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){ +- if(j==cand->component_id) +- break; ++ /* Doesn't fail because we just skip errors */ ++ return(0); ++ } + +- j++; +- } +- +- if(!comp){ +- r_log(LOG_ICE,LOG_ERR,"Peer answered with more components than we offered"); +- ABORT(R_BAD_DATA); +- } +- +- cand->component=comp; ++static int nr_ice_ctx_parse_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *pstream, char *candidate) ++ { ++ nr_ice_candidate *cand=0; ++ nr_ice_component *comp; ++ int j; ++ int r, _status; + +- TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp); ++ if(r=nr_ice_peer_candidate_from_attribute(pctx->ctx,candidate,pstream,&cand)) ++ ABORT(r); ++ if(cand->component_id-1>=pstream->component_ct){ ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) specified too many components",pctx->ctx->label,pctx->label); ++ ABORT(R_BAD_DATA); + } + +- STAILQ_INSERT_TAIL(&pctx->peer_streams,pstream,entry); ++ /* Not the fastest way to find a component, but it's what we got */ ++ j=1; ++ for(comp=STAILQ_FIRST(&pstream->components);comp;comp=STAILQ_NEXT(comp,entry)){ ++ if(j==cand->component_id) ++ break; ++ ++ j++; ++ } ++ ++ if(!comp){ ++ r_log(LOG_ICE,LOG_ERR,"Peer answered with more components than we offered"); ++ ABORT(R_BAD_DATA); ++ } ++ ++ cand->component=comp; ++ ++ TAILQ_INSERT_TAIL(&comp->candidates,cand,entry_comp); + + _status=0; +- abort: ++ abort: ++ if (_status) { ++ nr_ice_candidate_destroy(&cand); ++ } + return(_status); + } + ++ ++ ++int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate) ++ { ++ /* First need to find the stream. Because we don't have forward pointers, ++ iterate through all the peer streams to find one that matches us */ ++ nr_ice_media_stream *pstream; ++ int r,_status; ++ int needs_pairing = 0; ++ ++ pstream=STAILQ_FIRST(&pctx->peer_streams); ++ while(pstream) { ++ if (pstream->local_stream == stream) ++ break; ++ ++ pstream = STAILQ_NEXT(pstream, entry); ++ } ++ if (!pstream) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) has no stream matching stream %s",pctx->ctx->label,pctx->label,stream->label); ++ ABORT(R_NOT_FOUND); ++ } ++ ++ switch(pstream->ice_state) { ++ case NR_ICE_MEDIA_STREAM_UNPAIRED: ++ break; ++ case NR_ICE_MEDIA_STREAM_CHECKS_FROZEN: ++ case NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE: ++ needs_pairing = 1; ++ break; ++ default: ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) tried to trickle ICE in inappropriate state %d",pctx->ctx->label,pctx->label,stream->label,pstream->ice_state); ++ ABORT(R_ALREADY); ++ break; ++ } ++ ++ if(r=nr_ice_ctx_parse_candidate(pctx,pstream,candidate)){ ++ ABORT(r); ++ } ++ ++ /* If ICE is running (i.e., we are in FROZEN or ACTIVE states) ++ then we need to pair this new candidate. For now we ++ just re-pair the stream which is inefficient but still ++ fine because we suppress duplicate pairing */ ++ if (needs_pairing) { ++ if(r=nr_ice_media_stream_pair_candidates(pctx, stream, pstream)) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s), stream(%s) failed to pair trickle ICE candidates",pctx->ctx->label,pctx->label,stream->label); ++ ABORT(r); ++ } ++ } ++ ++ _status =0; ++ abort: ++ return(_status); ++ ++ } ++ ++ + int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx) + { + nr_ice_media_stream *stream; + int r,_status; + + if(STAILQ_EMPTY(&pctx->peer_streams)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) received no media stream attribributes",pctx->ctx->label,pctx->label); + ABORT(R_FAILED); + } + +@@ -177,21 +271,21 @@ + ABORT(r); + + stream=STAILQ_NEXT(stream,entry); + } + + _status=0; + abort: + return(_status); + } + +-static void nr_ice_peer_ctx_destroy_cb(int s, int how, void *cb_arg) ++static void nr_ice_peer_ctx_destroy_cb(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + nr_ice_media_stream *str1,*str2; + + RFREE(pctx->label); + RFREE(pctx->peer_ufrag); + RFREE(pctx->peer_pwd); + + STAILQ_FOREACH_SAFE(str1, &pctx->peer_streams, entry, str2){ + STAILQ_REMOVE(&pctx->peer_streams,str1,nr_ice_media_stream_,entry); +@@ -199,44 +293,79 @@ + } + + RFREE(pctx); + } + + int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp) + { + + if(!pctxp || !*pctxp) + return(0); +- ++ ++ /* Stop calling the handler */ ++ (*pctxp)->handler = 0; ++ + NR_ASYNC_SCHEDULE(nr_ice_peer_ctx_destroy_cb,*pctxp); + + *pctxp=0; + + return(0); + } + ++ + /* Start the checks for the first media stream (S 5.7) + The rest remain FROZEN */ + int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx) + { ++ return nr_ice_peer_ctx_start_checks2(pctx, 0); ++ } ++ ++/* Start checks for some media stream. ++ ++ If allow_non_first == 0, then we only look at the first stream, ++ which is 5245-complaint. ++ ++ If allow_non_first == 1 then we find the first non-empty stream ++ This is not compliant with RFC 5245 but is necessary to make trickle ICE ++ work plausibly ++*/ ++int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first) ++ { + int r,_status; + nr_ice_media_stream *stream; + + stream=STAILQ_FIRST(&pctx->peer_streams); + if(!stream) + ABORT(R_FAILED); + ++ while (stream) { ++ if(!TAILQ_EMPTY(&stream->check_list)) ++ break; ++ ++ if(!allow_non_first){ ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) first stream has empty check list",pctx->ctx->label,pctx->label); ++ ABORT(R_FAILED); ++ } ++ ++ stream=STAILQ_NEXT(stream, entry); ++ } ++ ++ if (!stream) { ++ r_log(LOG_ICE,LOG_ERR,"ICE(%s): peer (%s) no streams with non-empty check lists",pctx->ctx->label,pctx->label); ++ ABORT(R_NOT_FOUND); ++ } ++ + if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FROZEN) { +- if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream)) +- ABORT(r); +- if(r=nr_ice_media_stream_start_checks(pctx,stream)) +- ABORT(r); ++ if(r=nr_ice_media_stream_unfreeze_pairs(pctx,stream)) ++ ABORT(r); ++ if(r=nr_ice_media_stream_start_checks(pctx,stream)) ++ ABORT(r); + } + + _status=0; + abort: + return(_status); + } + + #ifndef NDEBUG + int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out) + { +@@ -253,26 +382,28 @@ + stream=STAILQ_NEXT(stream,entry); + } + fprintf(out,"==========================================\n"); + + _status=0; + abort: + return(_status); + } + #endif + +-static void nr_ice_peer_ctx_fire_done(int s, int how, void *cb_arg) ++static void nr_ice_peer_ctx_fire_done(NR_SOCKET s, int how, void *cb_arg) + { + nr_ice_peer_ctx *pctx=cb_arg; + + /* Fire the handler callback to say we're done */ +- pctx->handler->vtbl->ice_completed(pctx->handler->obj, pctx); ++ if (pctx->handler) { ++ pctx->handler->vtbl->ice_completed(pctx->handler->obj, pctx); ++ } + } + + + /* OK, a stream just went ready. Examine all the streams to see if we're + maybe miraculously done */ + int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream) + { + int _status; + nr_ice_media_stream *str; + int failed=0; +@@ -365,21 +496,24 @@ + NR_TRANSPORT_ADDR_CMP_MODE_ALL)) + break; + + cand=TAILQ_NEXT(cand,entry_comp); + } + + if(!cand) + ABORT(R_REJECTED); + + /* OK, there's a match. Call the handler */ +- r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label); + +- pctx->handler->vtbl->msg_recvd(pctx->handler->obj, +- pctx,comp->stream,comp->component_id,data,len); ++ if (pctx->handler) { ++ r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Delivering data", pctx->label); ++ ++ pctx->handler->vtbl->msg_recvd(pctx->handler->obj, ++ pctx,comp->stream,comp->component_id,data,len); ++ } + + _status=0; + abort: + return(_status); + } + + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.h src/ice/ice_peer_ctx.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_peer_ctx.h 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_peer_ctx.h 2012-10-06 08:30:22.000000000 -0700 +@@ -33,23 +33,21 @@ + + + #ifndef _ice_peer_ctx_h + #define _ice_peer_ctx_h + #ifdef __cplusplus + using namespace std; + extern "C" { + #endif /* __cplusplus */ + + struct nr_ice_peer_ctx_ { +- int state; + char *label; +- + nr_ice_ctx *ctx; + nr_ice_handler *handler; + + UCHAR controlling; /* 1 for controlling, 0 for controlled */ + UINT8 tiebreaker; + + char *peer_ufrag; + char *peer_pwd; + int peer_lite; + int peer_ice_mismatch; +@@ -59,23 +57,26 @@ + int waiting_pairs; + + STAILQ_ENTRY(nr_ice_peer_ctx_) entry; + }; + + typedef STAILQ_HEAD(nr_ice_peer_ctx_head_, nr_ice_peer_ctx_) nr_ice_peer_ctx_head; + + int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label, nr_ice_peer_ctx **pctxp); + int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp); + int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct); ++int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand); ++ + int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx); + int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct); + int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx); ++int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first); + int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out); + int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx); + int nr_ice_peer_ctx_stream_done(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream); + int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp); + int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len); + #ifdef __cplusplus + } + #endif /* __cplusplus */ + #endif + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_socket.c src/ice/ice_socket.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/ice/ice_socket.c 2012-09-16 16:26:08.000000000 -0700 ++++ src/ice/ice_socket.c 2012-10-06 08:30:22.000000000 -0700 +@@ -216,28 +216,34 @@ + RFREE(s1); + } + + RFREE(isock); + + return(0); + } + + int nr_ice_socket_close(nr_ice_socket *isock) + { ++#ifdef NR_SOCKET_IS_VOID_PTR ++ NR_SOCKET fd=NULL; ++ NR_SOCKET no_socket = NULL; ++#else + NR_SOCKET fd=-1; ++ NR_SOCKET no_socket = -1; ++#endif + + if (!isock||!isock->sock) + return(0); + + nr_socket_getfd(isock->sock,&fd); + assert(isock->sock!=0); +- if(fd!=-1){ ++ if(fd != no_socket){ + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_READ); + NR_ASYNC_CANCEL(fd,NR_ASYNC_WAIT_WRITE); + nr_socket_destroy(&isock->sock); + } + + return(0); + } + + int nr_ice_socket_register_stun_client(nr_ice_socket *sock, nr_stun_client_ctx *srv,void **handle) + { +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/net/nr_socket.h src/net/nr_socket.h +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/net/nr_socket.h 2012-09-16 16:26:09.000000000 -0700 ++++ src/net/nr_socket.h 2012-10-06 08:30:22.000000000 -0700 +@@ -38,21 +38,23 @@ + #include <sys/types.h> + #ifdef WIN32 + #include <winsock2.h> + #include <ws2tcpip.h> + #else + #include <sys/socket.h> + #endif + + #include "transport_addr.h" + +-#ifdef WIN32 ++#ifdef __cplusplus ++#define restrict ++#elif defined(WIN32) + #define restrict __restrict + #endif + + typedef struct nr_socket_vtbl_ { + int (*destroy)(void **obj); + int (*ssendto)(void *obj,const void *msg, size_t len, int flags, + nr_transport_addr *addr); + int (*srecvfrom)(void *obj,void * restrict buf, size_t maxlen, size_t *len, int flags, + nr_transport_addr *addr); + int (*getfd)(void *obj, NR_SOCKET *fd); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/net/transport_addr_reg.c src/net/transport_addr_reg.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/net/transport_addr_reg.c 2012-09-16 16:26:09.000000000 -0700 ++++ src/net/transport_addr_reg.c 2012-10-06 08:30:22.000000000 -0700 +@@ -29,25 +29,27 @@ + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + + + static char *RCSSTRING __UNUSED__="$Id: transport_addr_reg.c,v 1.2 2008/04/28 17:59:03 ekr Exp $"; + + #include <csi_platform.h> + #include <stdio.h> ++#include <string.h> + #include <memory.h> + #include <sys/types.h> + #ifdef WIN32 + #include <winsock2.h> + #else ++#include <strings.h> + #include <unistd.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #endif + #include <assert.h> + #include "nr_api.h" + #include "transport_addr.h" + #include "transport_addr_reg.h" + +@@ -83,20 +85,22 @@ + + if ((r=NR_reg_get2_uint2(prefix, "port", &port))) { + if (r != R_NOT_FOUND) + ABORT(r); + port = 0; + } + + if ((r=NR_reg_alloc2_string(prefix, "protocol", &protocol))) { + if (r != R_NOT_FOUND) + ABORT(r); ++ p = IPPROTO_UDP; ++ + protocol = 0; + } + else { + if (!strcasecmp("tcp", protocol)) + p = IPPROTO_TCP; + else if (!strcasecmp("udp", protocol)) + p = IPPROTO_UDP; + else + ABORT(R_BAD_DATA); + } +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/addrs.c src/stun/addrs.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/addrs.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/addrs.c 2012-10-06 09:42:43.000000000 -0700 +@@ -46,20 +46,22 @@ + #include <sys/sysctl.h> + #include <sys/param.h> + #include <sys/socket.h> + #include <sys/syslog.h> + #include <net/if.h> + #ifndef LINUX + #include <net/if_var.h> + #include <net/if_dl.h> + #include <net/if_types.h> + #include <sys/sockio.h> ++#else ++#include <linux/if.h> + #endif + #include <net/route.h> + + /* IP */ + #include <netinet/in.h> + #ifdef LINUX + #include "sys/ioctl.h" + #else + #include <netinet/in_var.h> + #endif +@@ -105,20 +107,23 @@ + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + #include <err.h> + + static void stun_rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *); + static int stun_grab_addrs(char *name, int addrcount, + struct ifa_msghdr *ifam, + nr_transport_addr addrs[], int maxaddrs, int *count); ++static int ++nr_stun_is_duplicate_addr(nr_transport_addr addrs[], int count, nr_transport_addr *addr); ++ + + /* + * Expand the compacted form of addresses as returned via the + * configuration read via sysctl(). + */ + #define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + + static void +@@ -135,21 +140,21 @@ + continue; + rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; + ADVANCE(cp, sa); + } + } + + static int + stun_grab_addrs(char *name, int addrcount, struct ifa_msghdr *ifam, nr_transport_addr addrs[], int maxaddrs, int *count) + { + int r,_status; +- NR_SOCKET s = -1; ++ int s = -1; + struct ifreq ifr; + struct rt_addrinfo info; + struct sockaddr_in *sin; + + ifr.ifr_addr.sa_family = AF_INET; + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); + + if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "unable to obtain addresses from socket"); + ABORT(R_FAILED); +@@ -180,21 +185,20 @@ + addrcount--; + + if (*count >= maxaddrs) { + r_log(NR_LOG_STUN, LOG_WARNING, "Address list truncated at %d out of entries", maxaddrs, maxaddrs+addrcount); + break; + } + + ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen); + } + +- + _status = 0; + abort: + if (s != -1) close(s); + return _status; + } + + static int + stun_get_mib_addrs(nr_transport_addr addrs[], int maxaddrs, int *count) + { + int _status; +@@ -551,44 +555,48 @@ + #else + + static int + stun_get_siocgifconf_addrs(nr_transport_addr addrs[], int maxaddrs, int *count) + { + struct ifconf ifc; + int _status; + int s = socket( AF_INET, SOCK_DGRAM, 0 ); + int len = 100 * sizeof(struct ifreq); + int r; ++ int e; ++ char *ptr; ++ int tl; ++ int n; ++ struct ifreq ifr2; + + char buf[ len ]; + + ifc.ifc_len = len; + ifc.ifc_buf = buf; + +- int e = ioctl(s,SIOCGIFCONF,&ifc); +- char *ptr = buf; +- int tl = ifc.ifc_len; +- int n=0; ++ e = ioctl(s,SIOCGIFCONF,&ifc); ++ ptr = buf; ++ tl = ifc.ifc_len; ++ n=0; + + while ( (tl > 0) && ( n < maxaddrs) ) + { + struct ifreq* ifr = (struct ifreq *)ptr; + + #ifdef LINUX +- int si = sizeof(ifr->ifr_name) + sizeof(ifr->ifr_addr); ++ int si = sizeof(struct ifreq); + #else + int si = sizeof(ifr->ifr_name) + MAX(ifr->ifr_addr.sa_len, sizeof(ifr->ifr_addr)); + #endif + tl -= si; + ptr += si; + +- struct ifreq ifr2; + ifr2 = *ifr; + + e = ioctl(s,SIOCGIFADDR,&ifr2); + if ( e == -1 ) + { + continue; + } + + //r_log(NR_LOG_STUN, LOG_ERR, "ioctl addr e = %d",e); + +@@ -603,21 +611,21 @@ + + close(s); + + *count = n; + + _status = 0; + return _status; + } + #endif + +-int ++static int + nr_stun_is_duplicate_addr(nr_transport_addr addrs[], int count, nr_transport_addr *addr) + { + int i; + int different; + + for (i = 0; i < count; ++i) { + different = nr_transport_addr_cmp(&addrs[i], addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL); + if (!different) + return 1; /* duplicate */ + } +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/nr_socket_turn.c src/stun/nr_socket_turn.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/nr_socket_turn.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/nr_socket_turn.c 2012-10-06 08:30:22.000000000 -0700 +@@ -246,17 +246,19 @@ + default: + assert(0); + break; + } + + return R_FAILED; + } + + static int nr_socket_turn_close(void *obj) + { ++#ifndef NDEBUG + nr_socket_turn *sturn=obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); ++#endif + + return 0; + } + + #endif /* USE_TURN */ +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_client_ctx.c src/stun/stun_client_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_client_ctx.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_client_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -38,21 +38,22 @@ + #include <string.h> + + #include <nr_api.h> + #include "stun.h" + #include "async_timer.h" + #include "registry.h" + #include "stun_reg.h" + #include "r_time.h" + + static int nr_stun_client_send_request(nr_stun_client_ctx *ctx); +-static void nr_stun_client_timer_expired_cb(int a, int b, void *cb_arg); ++static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg); ++static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password); + + int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx=0; + int r,_status; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx)))) +@@ -185,21 +186,21 @@ + ctx->finished_cb = 0; + ctx->cb_arg = 0; + ctx->request_ct = 0; + ctx->timeout_ms = 0; + + ctx->state = NR_STUN_CLIENT_STATE_INITTED; + + return 0; + } + +-static void nr_stun_client_timer_expired_cb(int a, int b, void *cb_arg) ++static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg) + { + int _status; + nr_stun_client_ctx *ctx=cb_arg; + struct timeval now; + INT8 ms_waited; + + /* Prevent this timer from being cancelled later */ + ctx->timer_handle=0; + + /* Shouldn't happen */ +@@ -387,21 +388,21 @@ + } + + _status=0; + abort: + if (_status) { + ctx->state=NR_STUN_CLIENT_STATE_FAILED; + } + return(_status); + } + +-int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password) ++static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password) + { + *password = (Data*)arg; + if (!arg) + return(R_NOT_FOUND); + return(0); + } + + int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr) + { + int r,_status; +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_codec.c src/stun/stun_codec.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_codec.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_codec.c 2012-10-06 08:30:22.000000000 -0700 +@@ -73,20 +73,22 @@ + static int nr_stun_decode_htonll(UCHAR *buf, int buflen, int *offset, UINT8 *data); + static int nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data); + + static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, int len, void *data, int max_bytes, int max_chars); + + static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); + static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, int attrlen, void *data); ++static int ++nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data); + + + int + nr_stun_encode_htons(UINT2 data, int buflen, UCHAR *buf, int *offset) + { + UINT2 d = htons(data); + + if (*offset + sizeof(d) >= buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d >= %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; +@@ -632,21 +634,21 @@ + } + + fingerprint->checksum = checksum ^ 0x5354554e; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum); + + fingerprint->valid = 1; + return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen); + } + +-int ++static int + nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, int attrlen, UCHAR *buf, int offset, int buflen, void *data) + { + int r,_status; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + int length; + UINT4 checksum; + + if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum))) + ABORT(r); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_proc.c src/stun/stun_proc.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_proc.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_proc.c 2012-10-06 08:30:22.000000000 -0700 +@@ -43,20 +43,22 @@ + #include <time.h> + #else /* UNIX */ + #include <string.h> + #endif /* end UNIX */ + #include <assert.h> + + #include "stun.h" + #include "stun_reg.h" + #include "registry.h" + ++static int ++nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res); + + /* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */ + int + nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg) + { + int _status; + nr_stun_message_attribute *attr; + + #ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* if this message was generated by an RFC 3489 impementation, +@@ -371,21 +373,21 @@ + /* nothing to check in this case */ + break; + #endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; + } + +-int ++static int + nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res) + { + int r,_status; + char *realm = 0; + char *nonce; + UINT2 size; + + if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm))) + ABORT(r); + +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_server_ctx.c src/stun/stun_server_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_server_ctx.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_server_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -114,21 +114,21 @@ + STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry); + + _status=0; + abort: + if(_status){ + nr_stun_server_destroy_client(clnt); + } + return(_status); + } + +-int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password) ++static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password) + { + int _status; + nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg; + nr_stun_server_client *clnt = 0; + nr_stun_message_attribute *username_attribute; + + if ((nr_stun_get_message_client(ctx, msg, &clnt))) { + if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) { + r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_util.c src/stun/stun_util.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/stun_util.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/stun_util.c 2012-10-06 08:30:22.000000000 -0700 +@@ -94,21 +94,20 @@ + _status = 0; + abort: + return _status; + } + + int + nr_stun_find_local_addresses(nr_transport_addr addrs[], int maxaddrs, int *count) + { + int r,_status; + NR_registry *children = 0; +- int i; + + if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count))) + if (r == R_NOT_FOUND) + *count = 0; + else + ABORT(r); + + if (*count == 0) { + if ((r=nr_stun_get_addrs(addrs, maxaddrs, 1, count))) + ABORT(r); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/turn_client_ctx.c src/stun/turn_client_ctx.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/stun/turn_client_ctx.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/stun/turn_client_ctx.c 2012-10-06 08:30:22.000000000 -0700 +@@ -55,21 +55,24 @@ + }; + + static int TURN_PHASE_MODE[NUMBER_OF_STUN_CTX] = { + NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST1, + NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST2, + NR_TURN_CLIENT_MODE_SET_ACTIVE_DEST_REQUEST + }; + + + static int nr_turn_client_next_action(nr_turn_client_ctx *ctx, int stun_ctx_state); +-static void nr_turn_client_cb(int s, int how, void *cb_arg); ++static void nr_turn_client_cb(NR_SOCKET s, int how, void *cb_arg); ++static int ++nr_turn_client_prepare_start(nr_turn_client_ctx *ctx, char *username, Data *password, UINT4 bandwidth_kbps, UINT4 lifetime_secs, NR_async_cb finished_cb, void *cb_arg); ++ + + int + nr_turn_client_next_action(nr_turn_client_ctx *ctx, int stun_ctx_state) + { + int r,_status; + + assert(ctx->phase >= -1 && ctx->phase < NUMBER_OF_STUN_CTX); + + switch (ctx->state) { + //case NR_TURN_CLIENT_STATE_ALLOCATING: +@@ -147,21 +150,21 @@ + * because as a side effect this ctx may be operated on in the + * callback */ + finished_cb(0,0,ctx->cb_arg); + } + } + + return(_status); + } + + void +-nr_turn_client_cb(int s, int how, void *cb_arg) ++nr_turn_client_cb(NR_SOCKET s, int how, void *cb_arg) + { + int r,_status; + nr_turn_client_ctx *ctx = (nr_turn_client_ctx*)cb_arg; + nr_stun_client_ctx *stun_ctx = ctx->stun_ctx[ctx->phase]; + + assert(ctx->phase >= 0); + + if ((r=nr_turn_client_next_action(ctx, stun_ctx->state))) + ABORT(r); + +@@ -234,21 +237,21 @@ + + RFREE(ctx->username); + r_data_destroy(&ctx->password); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + +-int ++static int + nr_turn_client_prepare_start(nr_turn_client_ctx *ctx, char *username, Data *password, UINT4 bandwidth_kbps, UINT4 lifetime_secs, NR_async_cb finished_cb, void *cb_arg) + { + int r,_status; + nr_stun_client_allocate_request1_params *allocate_request1 = 0; + nr_stun_client_allocate_request2_params *allocate_request2 = 0; + nr_stun_client_allocate_response1_results *allocate_response1 = 0; + // nr_stun_client_allocate_response2_results *allocate_response2; + + if (ctx->state != NR_TURN_CLIENT_STATE_INITTED) + ABORT(R_NOT_PERMITTED); +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/util/ice_util.c src/util/ice_util.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/util/ice_util.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/util/ice_util.c 2012-10-06 08:30:22.000000000 -0700 +@@ -31,20 +31,21 @@ + */ + + #include <stdarg.h> + + + static char *RCSSTRING __UNUSED__="$Id: ice_util.c,v 1.2 2008/04/28 17:59:05 ekr Exp $"; + + #include <stdarg.h> + #include <string.h> + #include "nr_api.h" ++#include "ice_util.h" + + int nr_concat_strings(char **outp,...) + { + va_list ap; + char *s,*out=0; + int len=0; + int _status; + + va_start(ap,outp); + while(s=va_arg(ap,char *)){ +diff -U10 -r /Users/ekr/dev/mtransport-import-references/nICEr/src/util/mbslen.c src/util/mbslen.c +--- /Users/ekr/dev/mtransport-import-references/nICEr/src/util/mbslen.c 2012-09-16 16:26:10.000000000 -0700 ++++ src/util/mbslen.c 2012-10-06 08:31:01.000000000 -0700 +@@ -56,21 +56,21 @@ + { + #ifdef DARWIN + static locale_t loc = 0; + static int initialized = 0; + #endif /* DARWIN */ + #ifdef WIN32 + char *my_locale=0; + unsigned int i; + #endif /* WIN32 */ + int _status; +- int nbytes; ++ size_t nbytes; + int nchars; + mbstate_t mbs; + + #ifdef DARWIN + if (! initialized) { + initialized = 1; + loc = newlocale(LC_CTYPE_MASK, "UTF-8", LC_GLOBAL_LOCALE); + } + + if (loc == 0) { +@@ -102,25 +102,28 @@ + + memset(&mbs, 0, sizeof(mbs)); + nchars = 0; + + #ifdef DARWIN + while (*s != '\0' && (nbytes = mbrlen_l(s, strlen(s), &mbs, loc)) != 0) + #else + while (*s != '\0' && (nbytes = mbrlen(s, strlen(s), &mbs)) != 0) + #endif /* DARWIN */ + { +- assert(nbytes >= 0); +- if (nbytes == (size_t)-1) /* should never happen */ ++ if (nbytes == (size_t)-1) /* should never happen */ { ++ assert(0); + ABORT(R_INTERNAL); +- if (nbytes == (size_t)-2) /* encoding error */ ++ } ++ if (nbytes == (size_t)-2) /* encoding error */ { ++ assert(0); + ABORT(R_BAD_DATA); ++ } + + s += nbytes; + ++nchars; + } + + *ncharsp = nchars; + + _status = 0; + abort: + #ifdef WIN32 |