From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../third_party/nICEr/src/stun/addrs-bsd.c | 110 ++ .../third_party/nICEr/src/stun/addrs-bsd.h | 13 + .../third_party/nICEr/src/stun/addrs-netlink.c | 285 ++++ .../third_party/nICEr/src/stun/addrs-netlink.h | 45 + .../third_party/nICEr/src/stun/addrs-win32.c | 210 +++ .../third_party/nICEr/src/stun/addrs-win32.h | 13 + .../transport/third_party/nICEr/src/stun/addrs.c | 176 +++ .../transport/third_party/nICEr/src/stun/addrs.h | 43 + .../nICEr/src/stun/nr_socket_buffered_stun.c | 656 +++++++++ .../nICEr/src/stun/nr_socket_buffered_stun.h | 66 + .../third_party/nICEr/src/stun/nr_socket_turn.c | 195 +++ .../third_party/nICEr/src/stun/nr_socket_turn.h | 48 + .../transport/third_party/nICEr/src/stun/stun.h | 218 +++ .../third_party/nICEr/src/stun/stun_build.c | 611 ++++++++ .../third_party/nICEr/src/stun/stun_build.h | 147 ++ .../third_party/nICEr/src/stun/stun_client_ctx.c | 888 +++++++++++ .../third_party/nICEr/src/stun/stun_client_ctx.h | 200 +++ .../third_party/nICEr/src/stun/stun_codec.c | 1550 ++++++++++++++++++++ .../third_party/nICEr/src/stun/stun_codec.h | 78 + .../third_party/nICEr/src/stun/stun_hint.c | 245 ++++ .../third_party/nICEr/src/stun/stun_hint.h | 44 + .../third_party/nICEr/src/stun/stun_msg.c | 364 +++++ .../third_party/nICEr/src/stun/stun_msg.h | 208 +++ .../third_party/nICEr/src/stun/stun_proc.c | 554 +++++++ .../third_party/nICEr/src/stun/stun_proc.h | 53 + .../third_party/nICEr/src/stun/stun_reg.h | 58 + .../third_party/nICEr/src/stun/stun_server_ctx.c | 468 ++++++ .../third_party/nICEr/src/stun/stun_server_ctx.h | 80 + .../third_party/nICEr/src/stun/stun_util.c | 352 +++++ .../third_party/nICEr/src/stun/stun_util.h | 62 + .../third_party/nICEr/src/stun/turn_client_ctx.c | 1277 ++++++++++++++++ .../third_party/nICEr/src/stun/turn_client_ctx.h | 161 ++ 32 files changed, 9478 insertions(+) create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h (limited to 'dom/media/webrtc/transport/third_party/nICEr/src/stun') diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c new file mode 100644 index 0000000000..ec2d084445 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.c @@ -0,0 +1,110 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if defined(BSD) || defined(DARWIN) +#include "addrs-bsd.h" +#include +#include +#include +#include "util.h" +#include "stun_util.h" +#include "util.h" +#include + +#include /* getifaddrs */ +#include /* getifaddrs */ +#include +#include +#include +#include +#include + +static int +stun_ifaddr_get_v6_flags(struct ifaddrs *ifaddr) +{ + if (ifaddr->ifa_addr->sa_family != AF_INET6) { + return 0; + } + + int flags = 0; + int s = socket(AF_INET6, SOCK_DGRAM, 0); + if (!s) { + r_log(NR_LOG_STUN, LOG_ERR, "socket(AF_INET6, SOCK_DGRAM, 0) failed, errno=%d", errno); + assert(0); + return 0; + } + struct in6_ifreq ifr6; + memset(&ifr6, 0, sizeof(ifr6)); + strncpy(ifr6.ifr_name, ifaddr->ifa_name, sizeof(ifr6.ifr_name)); + /* ifr_addr is a sockaddr_in6, ifa_addr is a sockaddr* */ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifaddr->ifa_addr; + ifr6.ifr_addr = *sin6; + if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) != -1) { + flags = ifr6.ifr_ifru.ifru_flags6; + } else { + r_log(NR_LOG_STUN, LOG_ERR, "ioctl(SIOCGIFAFLAG_IN6) failed, errno=%d", errno); + assert(0); + } + close(s); + return flags; +} + +static int +stun_ifaddr_is_disallowed_v6(int flags) { + return flags & (IN6_IFF_ANYCAST | IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED); +} + +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r,_status,flags; + struct ifaddrs* if_addrs_head=NULL; + struct ifaddrs* if_addr; + + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + if (getifaddrs(&if_addrs_head) == -1) { + r_log(NR_LOG_STUN, LOG_ERR, "getifaddrs error e = %d", errno); + ABORT(R_INTERNAL); + } + + if_addr = if_addrs_head; + + while (if_addr && *count < maxaddrs) { + /* This can be null */ + if (if_addr->ifa_addr) { + switch (if_addr->ifa_addr->sa_family) { + case AF_INET: + case AF_INET6: + flags = stun_ifaddr_get_v6_flags(if_addr); + if (!stun_ifaddr_is_disallowed_v6(flags)) { + if (r=nr_sockaddr_to_transport_addr(if_addr->ifa_addr, IPPROTO_UDP, 0, &(addrs[*count].addr))) { + r_log(NR_LOG_STUN, LOG_ERR, "nr_sockaddr_to_transport_addr error r = %d", r); + } else { + if (flags & IN6_IFF_TEMPORARY) { + addrs[*count].flags |= NR_ADDR_FLAG_TEMPORARY; + } + (void)strlcpy(addrs[*count].addr.ifname, if_addr->ifa_name, sizeof(addrs[*count].addr.ifname)); + ++(*count); + } + } + break; + default: + ; + } + } + + if_addr = if_addr->ifa_next; + } + + _status=0; +abort: + if (if_addrs_head) { + freeifaddrs(if_addrs_head); + } + return(_status); +} +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h new file mode 100644 index 0000000000..b575586f48 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-bsd.h @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef STUN_IFADDRS_BSD_H_ +#define STUN_IFADDRS_BSD_H_ + +#include "local_addr.h" + +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count); + +#endif /* STUN_IFADDRS_BSD_H_ */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c new file mode 100644 index 0000000000..8d22e5979e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.c @@ -0,0 +1,285 @@ +/* +Copyright (c) 2011, The WebRTC project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#if defined(LINUX) +#include +#include "addrs-netlink.h" +#include +#include +#include +#include "util.h" +#include "stun_util.h" +#include "util.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ANDROID +/* Work around an Android NDK < r8c bug */ +#undef __unused +#else +#include /* struct ifreq, IFF_POINTTOPOINT */ +#include /* struct iwreq */ +#include /* struct ethtool_cmd */ +#include /* SIOCETHTOOL */ +#endif /* ANDROID */ + + +struct netlinkrequest { + struct nlmsghdr header; + struct ifaddrmsg msg; +}; + +static const int kMaxReadSize = 4096; + +static void set_ifname(nr_local_addr *addr, struct ifaddrmsg* msg) { + assert(sizeof(addr->addr.ifname) > IF_NAMESIZE); + if_indextoname(msg->ifa_index, addr->addr.ifname); +} + +static int get_siocgifflags(nr_local_addr *addr) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + assert(0); + return 0; + } + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, addr->addr.ifname, IFNAMSIZ - 1); + int rc = ioctl(fd, SIOCGIFFLAGS, &ifr); + close(fd); + if (rc == -1) { + assert(0); + return 0; + } + return ifr.ifr_flags; +} + +static int set_sockaddr(nr_local_addr *addr, struct ifaddrmsg* msg, struct rtattr* rta) { + assert(rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL); + void *data = RTA_DATA(rta); + size_t len = RTA_PAYLOAD(rta); + if (msg->ifa_family == AF_INET) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(struct sockaddr_in)); + sa.sin_family = AF_INET; + memcpy(&sa.sin_addr, data, len); + return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr)); + } else if (msg->ifa_family == AF_INET6) { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(struct sockaddr_in6)); + sa.sin6_family = AF_INET6; + /* We do not set sin6_scope_id to ifa_index, because that is only valid for + * link local addresses, and we don't use those anyway */ + memcpy(&sa.sin6_addr, data, len); + return nr_sockaddr_to_transport_addr((struct sockaddr*)&sa, IPPROTO_UDP, 0, &(addr->addr)); + } + + return R_BAD_ARGS; +} + +static int +stun_ifaddr_is_disallowed_v6(int flags) { + return flags & (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | IFA_F_DEPRECATED); +} + +static int +stun_convert_netlink(nr_local_addr *addr, struct ifaddrmsg *address_msg, struct rtattr* rta) +{ + int r = set_sockaddr(addr, address_msg, rta); + if (r) { + r_log(NR_LOG_STUN, LOG_ERR, "set_sockaddr error r = %d", r); + return r; + } + + set_ifname(addr, address_msg); + + if (address_msg->ifa_flags & IFA_F_TEMPORARY) { + addr->flags |= NR_ADDR_FLAG_TEMPORARY; + } + + int flags = get_siocgifflags(addr); + if (flags & IFF_POINTOPOINT) + { + addr->interface.type = NR_INTERFACE_TYPE_UNKNOWN | NR_INTERFACE_TYPE_VPN; + /* TODO (Bug 896913): find backend network type of this VPN */ + } + +#if defined(LINUX) && !defined(ANDROID) + struct ethtool_cmd ecmd; + struct ifreq ifr; + struct iwreq wrq; + int e; + int s = socket(AF_INET, SOCK_DGRAM, 0); + + strncpy(ifr.ifr_name, addr->addr.ifname, sizeof(ifr.ifr_name)); + /* TODO (Bug 896851): interface property for Android */ + /* Getting ethtool for ethernet information. */ + ecmd.cmd = ETHTOOL_GSET; + /* In/out param */ + ifr.ifr_data = (void*)&ecmd; + + e = ioctl(s, SIOCETHTOOL, &ifr); + if (e == 0) + { + /* For wireless network, we won't get ethtool, it's a wired + * connection */ + addr->interface.type = NR_INTERFACE_TYPE_WIRED; +#ifdef DONT_HAVE_ETHTOOL_SPEED_HI + addr->interface.estimated_speed = ecmd.speed; +#else + addr->interface.estimated_speed = ((ecmd.speed_hi << 16) | ecmd.speed) * 1000; +#endif + } + + strncpy(wrq.ifr_name, addr->addr.ifname, sizeof(wrq.ifr_name)); + e = ioctl(s, SIOCGIWRATE, &wrq); + if (e == 0) + { + addr->interface.type = NR_INTERFACE_TYPE_WIFI; + addr->interface.estimated_speed = wrq.u.bitrate.value / 1000; + } + + close(s); + +#else + addr->interface.type = NR_INTERFACE_TYPE_UNKNOWN; + addr->interface.estimated_speed = 0; +#endif + return 0; +} + +int +stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int _status; + int fd = 0; + + /* Scope everything else since we're using ABORT. */ + { + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { + ABORT(R_INTERNAL); + } + + struct netlinkrequest ifaddr_request; + memset(&ifaddr_request, 0, sizeof(ifaddr_request)); + ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; + ifaddr_request.header.nlmsg_type = RTM_GETADDR; + ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + + ssize_t bytes = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); + if ((size_t)bytes != ifaddr_request.header.nlmsg_len) { + ABORT(R_INTERNAL); + } + + char buf[kMaxReadSize]; + ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); + while ((amount_read > 0) && (*count != maxaddrs)) { + struct nlmsghdr* header = (struct nlmsghdr*)&buf[0]; + size_t header_size = (size_t)amount_read; + for ( ; NLMSG_OK(header, header_size) && (*count != maxaddrs); + header = NLMSG_NEXT(header, header_size)) { + switch (header->nlmsg_type) { + case NLMSG_DONE: + /* Success. Return. */ + close(fd); + return 0; + case NLMSG_ERROR: + ABORT(R_INTERNAL); + case RTM_NEWADDR: { + struct ifaddrmsg* address_msg = + (struct ifaddrmsg*)NLMSG_DATA(header); + struct rtattr* rta = IFA_RTA(address_msg); + ssize_t payload_len = IFA_PAYLOAD(header); + bool found = false; + while (RTA_OK(rta, payload_len)) { + // This is a bit convoluted. IFA_ADDRESS and IFA_LOCAL are the + // same thing except when using a POINTTOPOINT interface, in + // which case IFA_LOCAL is the local address, and IFA_ADDRESS is + // the remote address. In a reasonable world, that would mean we + // could just use IFA_LOCAL all the time. Sadly, IFA_LOCAL is not + // always set (IPv6 in particular). So, we have to be on the + // lookout for both, and prefer IFA_LOCAL when present. + if (rta->rta_type == IFA_ADDRESS || rta->rta_type == IFA_LOCAL) { + int family = address_msg->ifa_family; + if ((family == AF_INET || family == AF_INET6) && + !stun_ifaddr_is_disallowed_v6(address_msg->ifa_flags) && + !stun_convert_netlink(&addrs[*count], address_msg, rta)) { + found = true; + if (rta->rta_type == IFA_LOCAL) { + // IFA_LOCAL is what we really want; if we find it we're + // done. If this is IFA_ADDRESS instead, we do not proceed + // yet, and allow a subsequent IFA_LOCAL to overwrite what + // we just put in |addrs|. + break; + } + } + } + /* TODO: Use IFA_LABEL instead of if_indextoname? We would need + * to remember how many nr_local_addr we've converted for this + * ifaddrmsg, and set the label on all of them. */ + rta = RTA_NEXT(rta, payload_len); + } + + if (found) { + ++(*count); + } + break; + } + } + } + amount_read = recv(fd, &buf, kMaxReadSize, 0); + } + } + + _status=0; +abort: + close(fd); + return(_status); +} + +#endif /* defined(LINUX) */ diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h new file mode 100644 index 0000000000..b2b07bddc9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-netlink.h @@ -0,0 +1,45 @@ +/* +Copyright (c) 2011, The WebRTC project authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef WEBRTC_BASE_IFADDRS_NETLINK_H_ +#define WEBRTC_BASE_IFADDRS_NETLINK_H_ + +#include +#include +#include "local_addr.h" + +/* for platforms with netlink (android and linux) */ +/* Filters out things like deprecated addresses, and stuff that doesn't pass + * IPv6 duplicate address detection */ +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count); +#endif /* WEBRTC_BASE_IFADDRS_NETLINK_H_ */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c new file mode 100644 index 0000000000..4fdf5e5d44 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.c @@ -0,0 +1,210 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef WIN32 + +#include "addrs-win32.h" +#include +#include +#include +#include "util.h" +#include "stun_util.h" +#include "util.h" +#include +#include "nr_crypto.h" + +#include +#include +#include + +#define WIN32_MAX_NUM_INTERFACES 20 + +#define NR_MD5_HASH_LENGTH 16 + +#define _NR_MAX_KEY_LENGTH 256 +#define _NR_MAX_NAME_LENGTH 512 + +#define _ADAPTERS_BASE_REG "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +static int nr_win32_get_adapter_friendly_name(char *adapter_GUID, char **friendly_name) +{ + int r,_status; + HKEY adapter_reg; + TCHAR adapter_key[_NR_MAX_KEY_LENGTH]; + TCHAR keyval_buf[_NR_MAX_KEY_LENGTH]; + TCHAR adapter_GUID_tchar[_NR_MAX_NAME_LENGTH]; + DWORD keyval_len, key_type; + size_t converted_chars, newlen; + char *my_fn = 0; + +#ifdef _UNICODE + mbstowcs_s(&converted_chars, adapter_GUID_tchar, strlen(adapter_GUID)+1, + adapter_GUID, _TRUNCATE); +#else + strlcpy(adapter_GUID_tchar, adapter_GUID, _NR_MAX_NAME_LENGTH); +#endif + + _tcscpy_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT(_ADAPTERS_BASE_REG)); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\")); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, adapter_GUID_tchar); + _tcscat_s(adapter_key, _NR_MAX_KEY_LENGTH, TEXT("\\Connection")); + + r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, adapter_key, 0, KEY_READ, &adapter_reg); + + if (r != ERROR_SUCCESS) { + r_log(NR_LOG_STUN, LOG_ERR, "Got error %d opening adapter reg key\n", r); + ABORT(R_INTERNAL); + } + + keyval_len = sizeof(keyval_buf); + r = RegQueryValueEx(adapter_reg, TEXT("Name"), NULL, &key_type, + (BYTE *)keyval_buf, &keyval_len); + + RegCloseKey(adapter_reg); + +#ifdef UNICODE + newlen = wcslen(keyval_buf)+1; + my_fn = (char *) RCALLOC(newlen); + if (!my_fn) { + ABORT(R_NO_MEMORY); + } + wcstombs_s(&converted_chars, my_fn, newlen, keyval_buf, _TRUNCATE); +#else + my_fn = r_strdup(keyval_buf); +#endif + + *friendly_name = my_fn; + _status=0; + +abort: + if (_status) { + if (my_fn) free(my_fn); + } + return(_status); +} + +static int stun_win32_address_disallowed(IP_ADAPTER_UNICAST_ADDRESS *addr) +{ + return (addr->DadState != NldsPreferred) && + (addr->DadState != IpDadStatePreferred); +} + +static int stun_win32_address_temp_v6(IP_ADAPTER_UNICAST_ADDRESS *addr) +{ + return (addr->Address.lpSockaddr->sa_family == AF_INET6) && + (addr->SuffixOrigin == IpSuffixOriginRandom); +} + +int +stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r, _status; + PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL, tmpAddress = NULL; + // recomended per https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx + static const ULONG initialBufLen = 15000; + ULONG buflen = initialBufLen; + char bin_hashed_ifname[NR_MD5_HASH_LENGTH]; + char hex_hashed_ifname[MAXIFNAME]; + int n = 0; + + *count = 0; + + if (maxaddrs <= 0) + ABORT(R_BAD_ARGS); + + /* According to MSDN (see above) we have try GetAdapterAddresses() multiple times */ + for (n = 0; n < 5; n++) { + AdapterAddresses = (PIP_ADAPTER_ADDRESSES) RMALLOC(buflen); + if (AdapterAddresses == NULL) { + r_log(NR_LOG_STUN, LOG_ERR, "Error allocating buf for GetAdaptersAddresses()"); + ABORT(R_NO_MEMORY); + } + + r = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, NULL, AdapterAddresses, &buflen); + if (r == NO_ERROR) { + break; + } + r_log(NR_LOG_STUN, LOG_ERR, "GetAdaptersAddresses() returned error (%d)", r); + RFREE(AdapterAddresses); + AdapterAddresses = NULL; + } + + if (n >= 5) { + r_log(NR_LOG_STUN, LOG_ERR, "5 failures calling GetAdaptersAddresses()"); + ABORT(R_INTERNAL); + } + + n = 0; + + /* Loop through the adapters */ + + for (tmpAddress = AdapterAddresses; tmpAddress != NULL; tmpAddress = tmpAddress->Next) { + + if (tmpAddress->OperStatus != IfOperStatusUp) + continue; + + if ((tmpAddress->IfIndex != 0) || (tmpAddress->Ipv6IfIndex != 0)) { + IP_ADAPTER_UNICAST_ADDRESS *u = 0; + + if(r=nr_crypto_md5((UCHAR *)tmpAddress->FriendlyName, + wcslen(tmpAddress->FriendlyName) * sizeof(wchar_t), + bin_hashed_ifname)) + ABORT(r); + if(r=nr_bin2hex(bin_hashed_ifname, sizeof(bin_hashed_ifname), + hex_hashed_ifname)) + ABORT(r); + + for (u = tmpAddress->FirstUnicastAddress; u != 0; u = u->Next) { + SOCKET_ADDRESS *sa_addr = &u->Address; + + if ((sa_addr->lpSockaddr->sa_family != AF_INET) && + (sa_addr->lpSockaddr->sa_family != AF_INET6)) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized sa_family for address on adapter %lu", tmpAddress->IfIndex); + continue; + } + + if (stun_win32_address_disallowed(u)) { + continue; + } + + if ((r=nr_sockaddr_to_transport_addr((struct sockaddr*)sa_addr->lpSockaddr, IPPROTO_UDP, 0, &(addrs[n].addr)))) { + ABORT(r); + } + + strlcpy(addrs[n].addr.ifname, hex_hashed_ifname, sizeof(addrs[n].addr.ifname)); + if (tmpAddress->IfType == IF_TYPE_ETHERNET_CSMACD) { + addrs[n].interface.type = NR_INTERFACE_TYPE_WIRED; + } else if (tmpAddress->IfType == IF_TYPE_IEEE80211) { + /* Note: this only works for >= Win Vista */ + addrs[n].interface.type = NR_INTERFACE_TYPE_WIFI; + } else { + addrs[n].interface.type = NR_INTERFACE_TYPE_UNKNOWN; + } +#if (_WIN32_WINNT >= 0x0600) + /* Note: only >= Vista provide link speed information */ + addrs[n].interface.estimated_speed = tmpAddress->TransmitLinkSpeed / 1000; +#else + addrs[n].interface.estimated_speed = 0; +#endif + if (stun_win32_address_temp_v6(u)) { + addrs[n].flags |= NR_ADDR_FLAG_TEMPORARY; + } + + if (++n >= maxaddrs) + goto done; + } + } + } + + done: + *count = n; + _status = 0; + + abort: + RFREE(AdapterAddresses); + return _status; +} + +#endif //WIN32 + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h new file mode 100644 index 0000000000..a00802192a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs-win32.h @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef STUN_IFADDRS_WIN32_H_ +#define STUN_IFADDRS_WIN32_H_ + +#include "local_addr.h" + +int stun_getaddrs_filtered(nr_local_addr addrs[], int maxaddrs, int *count); + +#endif /* STUN_IFADDRS_WIN32_H_ */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c new file mode 100644 index 0000000000..362b7d828e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.c @@ -0,0 +1,176 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#ifdef WIN32 +#include "addrs-win32.h" +#elif defined(BSD) || defined(DARWIN) +#include "addrs-bsd.h" +#else +#include "addrs-netlink.h" +#endif + +#include "stun.h" +#include "addrs.h" +#include "util.h" + +static int +nr_stun_is_duplicate_addr(nr_local_addr addrs[], int count, nr_local_addr *addr) +{ + int i; + int different; + + for (i = 0; i < count; ++i) { + different = nr_transport_addr_cmp(&addrs[i].addr, &(addr->addr), + NR_TRANSPORT_ADDR_CMP_MODE_ALL); + if (!different) + return 1; /* duplicate */ + } + + return 0; +} + +int +nr_stun_filter_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count) +{ + int r, _status; + nr_local_addr *tmp = 0; + int i; + int n; + /* We prefer temp ipv6 for their privacy properties. If we cannot get + * that, we prefer ipv6 that are not based on mac address. */ + int filter_mac_ipv6 = 0; + int filter_teredo_ipv6 = 0; + int filter_non_temp_ipv6 = 0; + + tmp = RMALLOC(*count * sizeof(*tmp)); + if (!tmp) + ABORT(R_NO_MEMORY); + + for (i = 0; i < *count; ++i) { + if (addrs[i].addr.ip_version == NR_IPV6) { + if (nr_transport_addr_is_teredo(&addrs[i].addr)) { + addrs[i].interface.type |= NR_INTERFACE_TYPE_TEREDO; + /* Prefer teredo over mac-based address. Probably will never see + * both. */ + filter_mac_ipv6 = 1; + } else { + filter_teredo_ipv6 = 1; + } + + if (!nr_transport_addr_is_mac_based(&addrs[i].addr)) { + filter_mac_ipv6 = 1; + } + + if (addrs[i].flags & NR_ADDR_FLAG_TEMPORARY) { + filter_non_temp_ipv6 = 1; + } + } + } + + n = 0; + for (i = 0; i < *count; ++i) { + if (nr_stun_is_duplicate_addr(tmp, n, &addrs[i])) { + /* skip addrs[i], it's a duplicate */ + } + else if (remove_loopback && nr_transport_addr_is_loopback(&addrs[i].addr)) { + /* skip addrs[i], it's a loopback */ + } + else if (remove_link_local && + nr_transport_addr_is_link_local(&addrs[i].addr)) { + /* skip addrs[i], it's a link-local address */ + } + else if (filter_mac_ipv6 && + nr_transport_addr_is_mac_based(&addrs[i].addr)) { + /* skip addrs[i], it's MAC based */ + } + else if (filter_teredo_ipv6 && + nr_transport_addr_is_teredo(&addrs[i].addr)) { + /* skip addrs[i], it's a Teredo address */ + } + else if (filter_non_temp_ipv6 && + (addrs[i].addr.ip_version == NR_IPV6) && + !(addrs[i].flags & NR_ADDR_FLAG_TEMPORARY)) { + /* skip addrs[i], it's a non-temporary ipv6, and we have a temporary */ + } + else { + /* otherwise, copy it to the temporary array */ + if ((r=nr_local_addr_copy(&tmp[n], &addrs[i]))) + ABORT(r); + ++n; + } + } + + *count = n; + + memset(addrs, 0, *count * sizeof(*addrs)); + /* copy temporary array into passed in/out array */ + for (i = 0; i < *count; ++i) { + if ((r=nr_local_addr_copy(&addrs[i], &tmp[i]))) + ABORT(r); + } + + _status = 0; + abort: + RFREE(tmp); + return _status; +} + +#ifndef USE_PLATFORM_NR_STUN_GET_ADDRS + +int +nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int _status=0; + int i; + char typestr[100]; + + // Ensure output records are always fully defined. See bug 1589990. + if (maxaddrs > 0) { + memset(addrs, 0, maxaddrs * sizeof(nr_local_addr)); + } + + _status = stun_getaddrs_filtered(addrs, maxaddrs, count); + + for (i = 0; i < *count; ++i) { + nr_local_addr_fmt_info_string(addrs+i,typestr,sizeof(typestr)); + r_log(NR_LOG_STUN, LOG_DEBUG, "Address %d: %s on %s, type: %s\n", + i,addrs[i].addr.as_string,addrs[i].addr.ifname,typestr); + } + + return _status; +} + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h new file mode 100644 index 0000000000..522bd92fd5 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/addrs.h @@ -0,0 +1,43 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _addrs_h_ +#define _addrs_h_ + +#include "transport_addr.h" +#include "local_addr.h" + +int nr_stun_get_addrs(nr_local_addr addrs[], int maxaddrs, int *count); +int nr_stun_filter_addrs(nr_local_addr addrs[], int remove_loopback, int remove_link_local, int *count); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c new file mode 100644 index 0000000000..4b5b92fb75 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c @@ -0,0 +1,656 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "p_buf.h" +#include "nr_socket.h" +#include "stun.h" +#include "nr_socket_buffered_stun.h" + +#define NR_MAX_FRAME_SIZE 0xFFFF + +typedef struct nr_frame_header_ { + UINT2 frame_length; + char data[0]; +} nr_frame_header; + +typedef struct nr_socket_buffered_stun_ { + nr_socket *inner; + nr_transport_addr remote_addr; + int connected; + + /* Read state */ + int read_state; +#define NR_ICE_SOCKET_READ_NONE 0 +#define NR_ICE_SOCKET_READ_HDR 1 +#define NR_ICE_SOCKET_READ_FAILED 2 + UCHAR *buffer; + size_t buffer_size; + size_t bytes_needed; + size_t bytes_read; + NR_async_cb readable_cb; + void *readable_cb_arg; + + /* Write state */ + nr_p_buf_ctx *p_bufs; + nr_p_buf_head pending_writes; + size_t pending; + size_t max_pending; + nr_framing_type framing_type; +} nr_socket_buffered_stun; + +static int nr_socket_buffered_stun_destroy(void **objp); +static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len, + int flags, const nr_transport_addr *to); +static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from); +static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd); +static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp); +static int nr_socket_buffered_stun_close(void *obj); +static int nr_socket_buffered_stun_connect(void *sock, const nr_transport_addr *addr); +static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written); +static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg); +static int nr_socket_buffered_stun_listen(void *obj, int backlog); +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp); + +static nr_socket_vtbl nr_socket_buffered_stun_vtbl={ + 2, + nr_socket_buffered_stun_destroy, + nr_socket_buffered_stun_sendto, + nr_socket_buffered_stun_recvfrom, + nr_socket_buffered_stun_getfd, + nr_socket_buffered_stun_getaddr, + nr_socket_buffered_stun_connect, + 0, + 0, + nr_socket_buffered_stun_close, + nr_socket_buffered_stun_listen, + nr_socket_buffered_stun_accept +}; + +void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg) +{ + nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj; + + buf_sock->readable_cb = readable_cb; + buf_sock->readable_cb_arg = readable_cb_arg; +} + +int nr_socket_buffered_set_connected_to(nr_socket *sock, nr_transport_addr *remote_addr) +{ + nr_socket_buffered_stun *buf_sock = (nr_socket_buffered_stun *)sock->obj; + int r, _status; + + if ((r=nr_transport_addr_copy(&buf_sock->remote_addr, remote_addr))) + ABORT(r); + + buf_sock->connected = 1; + + _status=0; +abort: + return(_status); +} + +int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, + nr_framing_type framing_type, nr_socket **sockp) +{ + int r, _status; + nr_socket_buffered_stun *sock = 0; + size_t frame_size; + + if (!(sock = RCALLOC(sizeof(nr_socket_buffered_stun)))) + ABORT(R_NO_MEMORY); + + sock->inner = inner; + sock->framing_type = framing_type; + + if ((r=nr_ip4_port_to_transport_addr(INADDR_ANY, 0, IPPROTO_TCP, &sock->remote_addr))) + ABORT(r); + + switch (framing_type) { + case ICE_TCP_FRAMING: + frame_size = sizeof(nr_frame_header); + sock->buffer_size = sizeof(nr_frame_header) + NR_MAX_FRAME_SIZE; + sock->bytes_needed = sizeof(nr_frame_header); + break; + case TURN_TCP_FRAMING: + frame_size = 0; + sock->buffer_size = NR_STUN_MAX_MESSAGE_SIZE; + sock->bytes_needed = sizeof(nr_stun_message_header); + break; + default: + assert(0); + ABORT(R_BAD_ARGS); + } + + /* TODO(ekr@rtfm.com): Check this */ + if (!(sock->buffer = RMALLOC(sock->buffer_size))) + ABORT(R_NO_MEMORY); + + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->connected = 0; + + STAILQ_INIT(&sock->pending_writes); + if ((r=nr_p_buf_ctx_create(sock->buffer_size, &sock->p_bufs))) + ABORT(r); + sock->max_pending = max_pending + frame_size; + + if ((r=nr_socket_create_int(sock, &nr_socket_buffered_stun_vtbl, sockp))) + ABORT(r); + + _status=0; +abort: + if (_status && sock) { + void *sock_v = sock; + sock->inner = 0; /* Give up ownership so we don't destroy */ + nr_socket_buffered_stun_destroy(&sock_v); + } + return(_status); +} + +/* Note: This destroys the inner socket */ +int nr_socket_buffered_stun_destroy(void **objp) +{ + nr_socket_buffered_stun *sock; + NR_SOCKET fd; + + if (!objp || !*objp) + return 0; + + sock = (nr_socket_buffered_stun *)*objp; + *objp = 0; + + /* Free the buffer if needed */ + RFREE(sock->buffer); + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes); + nr_p_buf_ctx_destroy(&sock->p_bufs); + nr_socket_destroy(&sock->inner); + RFREE(sock); + + return 0; +} + +static int nr_socket_buffered_stun_sendto(void *obj,const void *msg, size_t len, + int flags, const nr_transport_addr *to) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + int r, _status; + size_t written; + nr_frame_header *frame = NULL; + + /* Check that we are writing to the connected address if + connected */ + if (!nr_transport_addr_is_wildcard(&sock->remote_addr)) { + if (nr_transport_addr_cmp(&sock->remote_addr, to, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(LOG_GENERIC, LOG_ERR, "Sendto on connected socket doesn't match"); + ABORT(R_BAD_DATA); + } + } + + if (sock->framing_type == ICE_TCP_FRAMING) { + + assert(len <= NR_MAX_FRAME_SIZE); + if (len > NR_MAX_FRAME_SIZE) + ABORT(R_FAILED); + + if (!(frame = RMALLOC(len + sizeof(nr_frame_header)))) + ABORT(R_NO_MEMORY); + + frame->frame_length = htons(len); + memcpy(frame->data, msg, len); + len += sizeof(nr_frame_header); + msg = frame; + } + + if ((r=nr_socket_buffered_stun_write(obj, msg, len, &written))) + ABORT(r); + + if (len != written) + ABORT(R_IO_ERROR); + + _status=0; +abort: + RFREE(frame); + return _status; +} + +static void nr_socket_buffered_stun_failed(nr_socket_buffered_stun *sock) + { + NR_SOCKET fd; + + sock->read_state = NR_ICE_SOCKET_READ_FAILED; + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + } + +static int nr_socket_buffered_stun_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from) +{ + int r, _status; + size_t bytes_read; + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + nr_frame_header *frame = (nr_frame_header *)sock->buffer; + size_t skip_hdr_size = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : 0; + + if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) { + ABORT(R_FAILED); + } + + while (sock->bytes_needed) { + /* Read all the expected bytes */ + assert(sock->bytes_needed <= sock->buffer_size - sock->bytes_read); + + if(r=nr_socket_read(sock->inner, + sock->buffer + sock->bytes_read, + sock->bytes_needed, &bytes_read, 0)) + ABORT(r); + + assert(bytes_read <= sock->bytes_needed); + sock->bytes_needed -= bytes_read; + sock->bytes_read += bytes_read; + + /* Unfinished */ + if (sock->bytes_needed) + ABORT(R_WOULDBLOCK); + + /* No more bytes expected */ + if (sock->read_state == NR_ICE_SOCKET_READ_NONE) { + size_t remaining_length; + if (sock->framing_type == ICE_TCP_FRAMING) { + if (sock->bytes_read < sizeof(nr_frame_header)) + ABORT(R_BAD_DATA); + remaining_length = ntohs(frame->frame_length); + } else { + int tmp_length; + + /* Parse the header */ + if (r = nr_stun_message_length(sock->buffer, sock->bytes_read, &tmp_length)) + ABORT(r); + assert(tmp_length >= 0); + if (tmp_length < 0) + ABORT(R_BAD_DATA); + remaining_length = tmp_length; + + } + /* Check to see if we have enough room */ + if ((sock->buffer_size - sock->bytes_read) < remaining_length) + ABORT(R_BAD_DATA); + + sock->read_state = NR_ICE_SOCKET_READ_HDR; + /* Set ourselves up to read the rest of the data */ + sock->bytes_needed = remaining_length; + } + } + + assert(skip_hdr_size <= sock->bytes_read); + if (skip_hdr_size > sock->bytes_read) + ABORT(R_BAD_DATA); + sock->bytes_read -= skip_hdr_size; + + if (maxlen < sock->bytes_read) + ABORT(R_BAD_ARGS); + + *len = sock->bytes_read; + memcpy(buf, sock->buffer + skip_hdr_size, sock->bytes_read); + + sock->bytes_read = 0; + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->bytes_needed = (sock->framing_type == ICE_TCP_FRAMING) ? sizeof(nr_frame_header) : sizeof(nr_stun_message_header); + + if ((r = nr_transport_addr_copy(from, &sock->remote_addr))) ABORT(r); + + _status=0; +abort: + if (_status && (_status != R_WOULDBLOCK)) { + nr_socket_buffered_stun_failed(sock); + } + + return(_status); +} + +static int nr_socket_buffered_stun_getfd(void *obj, NR_SOCKET *fd) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + return nr_socket_getfd(sock->inner, fd); +} + +static int nr_socket_buffered_stun_getaddr(void *obj, nr_transport_addr *addrp) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + return nr_socket_getaddr(sock->inner, addrp); +} + +static int nr_socket_buffered_stun_close(void *obj) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + NR_SOCKET fd; + + /* Cancel waiting on the socket */ + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + } + + return nr_socket_close(sock->inner); +} + +static int nr_socket_buffered_stun_listen(void *obj, int backlog) +{ + int r, _status; + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + + if (!sock->inner) + ABORT(R_FAILED); + + if ((r=nr_socket_listen(sock->inner, backlog))) + ABORT(r); + + _status=0; +abort: + return(_status); +} + + +static int nr_socket_buffered_stun_accept(void *obj, nr_transport_addr *addrp, nr_socket **sockp) +{ + nr_socket_buffered_stun *bsock = (nr_socket_buffered_stun *)obj; + + return nr_socket_accept(bsock->inner, addrp, sockp); +} + +static void nr_socket_buffered_stun_connected_cb(NR_SOCKET s, int how, void *arg) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg; + int r, _status; + NR_SOCKET fd; + + assert(!sock->connected); + + sock->connected = 1; + + if ((r=nr_socket_getfd(sock->inner, &fd))) + ABORT(r); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + + // once connected arm for read + if (sock->readable_cb) { + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, sock->readable_cb, sock->readable_cb_arg); + } + + if (sock->pending) { + r_log(LOG_GENERIC, LOG_INFO, "Invoking writable_cb on connected (%u)", (uint32_t) sock->pending); + nr_socket_buffered_stun_writable_cb(s, how, arg); + } + + _status=0; +abort: + if (_status) { + r_log(LOG_GENERIC, LOG_ERR, "Failure in nr_socket_buffered_stun_connected_cb: %d", _status); + + } +} + +static int nr_socket_buffered_stun_connect(void *obj, const nr_transport_addr *addr) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + int r, _status; + + if ((r=nr_transport_addr_copy(&sock->remote_addr, addr))) + ABORT(r); + + if ((r=nr_socket_connect(sock->inner, addr))) { + if (r == R_WOULDBLOCK) { + NR_SOCKET fd; + + if ((r=nr_socket_getfd(sock->inner, &fd))) + ABORT(r); + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_connected_cb, sock); + ABORT(R_WOULDBLOCK); + } + ABORT(r); + } else { + r_log(LOG_GENERIC, LOG_INFO, "Connected without blocking"); + sock->connected = 1; + } + + _status=0; +abort: + return(_status); +} + +static int nr_socket_buffered_stun_arm_writable_cb(nr_socket_buffered_stun *sock) +{ + int r, _status; + NR_SOCKET fd; + + if ((r=nr_socket_getfd(sock->inner, &fd))) + ABORT(r); + + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_WRITE, nr_socket_buffered_stun_writable_cb, sock); + + _status=0; +abort: + return(_status); +} + +static int nr_socket_buffered_stun_write(void *obj,const void *msg, size_t len, size_t *written) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)obj; + int already_armed = 0; + int r,_status; + size_t written2 = 0; + size_t original_len = len; + + /* Buffers are close to full, report error. Do this now so we never + get partial writes */ + if ((sock->pending + len) > sock->max_pending) { + r_log(LOG_GENERIC, LOG_INFO, "Write buffer for %s full (%u + %u > %u) - re-arming @%p", + sock->remote_addr.as_string, (uint32_t)sock->pending, (uint32_t)len, (uint32_t)sock->max_pending, + &(sock->pending)); + ABORT(R_WOULDBLOCK); + } + + + if (sock->connected && !sock->pending) { + r = nr_socket_write(sock->inner, msg, len, &written2, 0); + if (r) { + if (r != R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d", + sock->remote_addr.as_string, r); + ABORT(r); + } + r_log(LOG_GENERIC, LOG_INFO, "Write of %" PRIu64 " blocked for %s", + (uint64_t) len, sock->remote_addr.as_string); + + written2=0; + } + } else { + already_armed = 1; + } + + /* Buffer what's left */ + len -= written2; + + if (len) { + if ((r=nr_p_buf_write_to_chain(sock->p_bufs, &sock->pending_writes, + ((UCHAR *)msg) + written2, len))) { + r_log(LOG_GENERIC, LOG_ERR, "Write_to_chain error for %s - %d", + sock->remote_addr.as_string, r); + + ABORT(r); + } + + sock->pending += len; + } + + if (sock->pending) { + if (!already_armed) { + if ((r=nr_socket_buffered_stun_arm_writable_cb(sock))) + ABORT(r); + } + r_log(LOG_GENERIC, LOG_INFO, "Write buffer not empty for %s %u - %s armed (@%p),%s connected", + sock->remote_addr.as_string, (uint32_t)sock->pending, + already_armed ? "already" : "", &sock->pending, + sock->connected ? "" : " not"); + } + + *written = original_len; + + _status=0; +abort: + return _status; +} + +static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg) +{ + nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg; + int r,_status; + nr_p_buf *n1, *n2; + + if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) { + ABORT(R_FAILED); + } + + /* Try to flush */ + STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) { + size_t written = 0; + + if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset, + n1->length - n1->r_offset, + &written, 0))) { + + r_log(LOG_GENERIC, LOG_ERR, "Write error for %s - %d", + sock->remote_addr.as_string, r); + ABORT(r); + } + + n1->r_offset += written; + assert(sock->pending >= written); + sock->pending -= written; + + if (n1->r_offset < n1->length) { + /* We wrote something, but not everything */ + r_log(LOG_GENERIC, LOG_INFO, "Write in callback didn't write all (remaining %u of %u) for %s", + n1->length - n1->r_offset, n1->length, + sock->remote_addr.as_string); + ABORT(R_WOULDBLOCK); + } + + /* We are done with this p_buf */ + STAILQ_REMOVE_HEAD(&sock->pending_writes, entry); + nr_p_buf_free(sock->p_bufs, n1); + } + + assert(!sock->pending); + _status=0; +abort: + r_log(LOG_GENERIC, LOG_INFO, "Writable_cb %s (%u (%p) pending)", + sock->remote_addr.as_string, (uint32_t)sock->pending, &(sock->pending)); + if (_status && _status != R_WOULDBLOCK) { + r_log(LOG_GENERIC, LOG_ERR, "Failure in writable_cb: %d", _status); + nr_socket_buffered_stun_failed(sock); + /* Report this failure up; the only way to do this is a readable callback. + * Once the user tries to read (using nr_socket_buffered_stun_recvfrom), it + * will notice that there has been a failure. */ + if (sock->readable_cb) { + sock->readable_cb(s, NR_ASYNC_WAIT_READ, sock->readable_cb_arg); + } + } else if (sock->pending) { + nr_socket_buffered_stun_arm_writable_cb(sock); + } +} + +int nr_socket_buffered_stun_reset(nr_socket* sock_arg, nr_socket* new_inner) { + int r, _status; + NR_SOCKET fd; + + nr_socket_buffered_stun* sock = (nr_socket_buffered_stun*)sock_arg->obj; + + if (sock->inner && !nr_socket_getfd(sock->inner, &fd)) { + r_log(LOG_GENERIC, LOG_DEBUG, "In %s, canceling wait on old socket", __FUNCTION__); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_WRITE); + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); + } + + nr_socket_destroy(&sock->inner); + sock->inner = new_inner; + + sock->read_state = NR_ICE_SOCKET_READ_NONE; + sock->connected = 0; + + sock->bytes_read = 0; + sock->bytes_needed = (sock->framing_type == ICE_TCP_FRAMING) + ? sizeof(nr_frame_header) + : sizeof(nr_stun_message_header); + sock->pending = 0; + + nr_p_buf_free_chain(sock->p_bufs, &sock->pending_writes); + nr_p_buf_ctx_destroy(&sock->p_bufs); + + STAILQ_INIT(&sock->pending_writes); + + if ((r = nr_p_buf_ctx_create(sock->buffer_size, &sock->p_bufs))) { + ABORT(r); + } + + if ((r = nr_ip4_port_to_transport_addr(INADDR_ANY, 0, IPPROTO_TCP, + &sock->remote_addr))) { + ABORT(r); + } + + _status = 0; +abort: + return (_status); +} diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h new file mode 100644 index 0000000000..c635ee393d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_buffered_stun.h @@ -0,0 +1,66 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _nr_socket_buffered_stun_h +#define _nr_socket_buffered_stun_h + +#include "nr_socket.h" + +/* Wrapper socket which provides buffered STUN-oriented I/O + + 1. Writes don't block and are automatically flushed when needed. + 2. All reads are in units of STUN messages + + This socket takes ownership of the inner socket |sock|. + */ + +typedef enum { + TURN_TCP_FRAMING=0, + ICE_TCP_FRAMING +} nr_framing_type; + +void nr_socket_buffered_stun_set_readable_cb(nr_socket *sock, + NR_async_cb readable_cb, void *readable_cb_arg); + +int nr_socket_buffered_stun_create(nr_socket *inner, int max_pending, + nr_framing_type framing_type, nr_socket **sockp); + +int nr_socket_buffered_set_connected_to(nr_socket *sock, + nr_transport_addr *remote_addr); + +int nr_socket_buffered_stun_reset(nr_socket *sock, nr_socket *new_inner); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c new file mode 100644 index 0000000000..1a0162b13e --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.c @@ -0,0 +1,195 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_TURN + +#include +#include +#include +#include +#include + +#include "stun.h" +#include "turn_client_ctx.h" +#include "nr_socket_turn.h" + + +static char *nr_socket_turn_magic_cookie = "nr_socket_turn"; + +typedef struct nr_socket_turn_ { + char *magic_cookie; + nr_turn_client_ctx *turn; +} nr_socket_turn; + + +static int nr_socket_turn_destroy(void **objp); +static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len, + int flags, const nr_transport_addr *to); +static int nr_socket_turn_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *from); +static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd); +static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp); +static int nr_socket_turn_close(void *obj); + +static nr_socket_vtbl nr_socket_turn_vtbl={ + 2, + nr_socket_turn_destroy, + nr_socket_turn_sendto, + nr_socket_turn_recvfrom, + nr_socket_turn_getfd, + nr_socket_turn_getaddr, + 0, + 0, + 0, + nr_socket_turn_close, + 0, + 0 +}; + +int nr_socket_turn_create(nr_socket **sockp) + { + int r,_status; + nr_socket_turn *sturn=0; + + if(!(sturn=RCALLOC(sizeof(nr_socket_turn)))) + ABORT(R_NO_MEMORY); + + sturn->magic_cookie = nr_socket_turn_magic_cookie; + + if(r=nr_socket_create_int(sturn, &nr_socket_turn_vtbl, sockp)) + ABORT(r); + + _status=0; + abort: + if(_status){ + nr_socket_turn_destroy((void **)&sturn); + } + return(_status); + } + +static int nr_socket_turn_destroy(void **objp) + { + int _status; + nr_socket_turn *sturn; + + if(!objp || !*objp) + return(0); + + sturn=*objp; + *objp=0; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + + /* we don't own the socket, so don't destroy it */ + + RFREE(sturn); + + _status=0; + return(_status); + } + +static int nr_socket_turn_sendto(void *obj,const void *msg, size_t len, + int flags, const nr_transport_addr *addr) + { + int r,_status; + nr_socket_turn *sturn=obj; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(sturn->turn); + + if ((r = nr_turn_client_send_indication(sturn->turn, msg, len, flags, + addr))) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +static int nr_socket_turn_recvfrom(void *obj,void * restrict buf, + size_t maxlen, size_t *len, int flags, nr_transport_addr *addr) + { + /* Reading from TURN sockets is done by the indication + processing code in turn_client_ctx. */ + assert(0); + + return(R_INTERNAL); + } + +static int nr_socket_turn_getfd(void *obj, NR_SOCKET *fd) + { + /* You should never directly be touching this fd. */ + assert(0); + + return(R_INTERNAL); + } + +static int nr_socket_turn_getaddr(void *obj, nr_transport_addr *addrp) + { + nr_socket_turn *sturn=obj; + int r, _status; + + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(sturn->turn); + + /* This returns the relayed address */ + if ((r=nr_turn_client_get_relayed_address(sturn->turn, addrp))) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +static int nr_socket_turn_close(void *obj) + { + /* No-op */ +#ifndef NDEBUG + nr_socket_turn *sturn=obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); +#endif + + return 0; + } + +int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx) +{ + nr_socket_turn *sturn=(nr_socket_turn*)sock->obj; + assert(sturn->magic_cookie == nr_socket_turn_magic_cookie); + assert(!sturn->turn); + + sturn->turn = ctx; + + return 0; +} + +#endif /* USE_TURN */ diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h new file mode 100644 index 0000000000..13506045a8 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/nr_socket_turn.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _nr_socket_turn_h +#define _nr_socket_turn_h + +#include "nr_socket.h" + +/* This is a partial implementation of an nr_socket wrapped + around TURN. It implements only the nr_socket features + actually used by the ICE stack. You can't, for instance, + read off the socket */ +int nr_socket_turn_create(nr_socket **sockp); +int nr_socket_turn_set_ctx(nr_socket *sock, nr_turn_client_ctx *ctx); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h new file mode 100644 index 0000000000..a32751d795 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun.h @@ -0,0 +1,218 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _STUN_H +#define _STUN_H + +#ifdef WIN32 +#include +#else +#include +#include +#ifndef LINUX +#include +#ifdef DARWIN +#include +#endif +#include +#include +#else +#include +#endif +#ifndef BSD +#include +#endif +#include +#ifndef LINUX +#include +#endif +#include +#include +#endif +#include + +#include "nr_api.h" +#include "stun_msg.h" +#include "stun_build.h" +#include "stun_codec.h" +#include "stun_hint.h" +#include "stun_util.h" +#include "nr_socket.h" +#include "stun_client_ctx.h" +#include "stun_server_ctx.h" +#include "stun_proc.h" + +#define NR_STUN_VERSION "rfc3489bis-11" +#define NR_STUN_PORT 3478 + +/* STUN attributes */ +#define NR_STUN_ATTR_MAPPED_ADDRESS 0x0001 +#define NR_STUN_ATTR_USERNAME 0x0006 +#define NR_STUN_ATTR_MESSAGE_INTEGRITY 0x0008 +#define NR_STUN_ATTR_ERROR_CODE 0x0009 +#define NR_STUN_ATTR_UNKNOWN_ATTRIBUTES 0x000A +#define NR_STUN_ATTR_REALM 0x0014 +#define NR_STUN_ATTR_NONCE 0x0015 +#define NR_STUN_ATTR_XOR_MAPPED_ADDRESS 0x0020 +#define NR_STUN_ATTR_SERVER 0x8022 +#define NR_STUN_ATTR_ALTERNATE_SERVER 0x8023 +#define NR_STUN_ATTR_FINGERPRINT 0x8028 + +/* for backwards compatibility with obsolete versions of the STUN spec */ +#define NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS 0x8020 + +#ifdef USE_STUND_0_96 +#define NR_STUN_ATTR_OLD_CHANGE_REQUEST 0x0003 +#endif /* USE_STUND_0_96 */ + +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE +/* for backwards compatibility with obsolete versions of the STUN spec */ +#define NR_STUN_ATTR_OLD_PASSWORD 0x0007 +#define NR_STUN_ATTR_OLD_RESPONSE_ADDRESS 0x0002 +#define NR_STUN_ATTR_OLD_SOURCE_ADDRESS 0x0004 +#define NR_STUN_ATTR_OLD_CHANGED_ADDRESS 0x0005 +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + +#ifdef USE_ICE +/* ICE attributes */ +#define NR_STUN_ATTR_PRIORITY 0x0024 +#define NR_STUN_ATTR_USE_CANDIDATE 0x0025 +#define NR_STUN_ATTR_ICE_CONTROLLED 0x8029 +#define NR_STUN_ATTR_ICE_CONTROLLING 0x802A +#endif /* USE_ICE */ + +#ifdef USE_TURN +/* TURN attributes */ +#define NR_STUN_ATTR_LIFETIME 0x000d +/* from an expired draft defined as optional, but in the required range */ +#define NR_STUN_ATTR_BANDWIDTH 0x0010 +#define NR_STUN_ATTR_XOR_PEER_ADDRESS 0x0012 +#define NR_STUN_ATTR_DATA 0x0013 +#define NR_STUN_ATTR_XOR_RELAY_ADDRESS 0x0016 +#define NR_STUN_ATTR_REQUESTED_TRANSPORT 0x0019 + +#define NR_STUN_ATTR_REQUESTED_TRANSPORT_UDP 17 +#endif /* USE_TURN */ + +/* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |M|M|M|M|M|C|M|M|M|C|M|M|M|M| + * |1|1|9|8|7|1|6|5|4|0|3|2|1|0| + * |1|0| | | | | | | | | | | | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Figure 3: Format of STUN Message Type Field + */ +#define NR_STUN_METHOD_TYPE_BITS(m) \ + ((((m) & 0xf80) << 2) | (((m) & 0x070) << 1) | ((m) & 0x00f)) + +#define NR_STUN_CLASS_TYPE_BITS(c) \ + ((((c) & 0x002) << 7) | (((c) & 0x001) << 4)) + +#define NR_STUN_GET_TYPE_METHOD(t) \ + ((((t) >> 2) & 0xf80) | (((t) >> 1) & 0x070) | ((t) & 0x00f)) + +#define NR_STUN_GET_TYPE_CLASS(t) \ + ((((t) >> 7) & 0x002) | (((t) >> 4) & 0x001)) + +#define NR_STUN_TYPE(m,c) (NR_STUN_METHOD_TYPE_BITS((m)) | NR_STUN_CLASS_TYPE_BITS((c))) + +/* building blocks for message types */ +#define NR_METHOD_BINDING 0x001 +#define NR_CLASS_REQUEST 0x0 +#define NR_CLASS_INDICATION 0x1 +#define NR_CLASS_RESPONSE 0x2 +#define NR_CLASS_ERROR_RESPONSE 0x3 + +/* define types for STUN messages */ +#define NR_STUN_MSG_BINDING_REQUEST NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_BINDING_INDICATION NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_INDICATION) +#define NR_STUN_MSG_BINDING_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_BINDING_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_BINDING, \ + NR_CLASS_ERROR_RESPONSE) + +#ifdef USE_TURN +/* building blocks for TURN message types */ +#define NR_METHOD_ALLOCATE 0x003 +#define NR_METHOD_REFRESH 0x004 + +#define NR_METHOD_SEND 0x006 +#define NR_METHOD_DATA 0x007 +#define NR_METHOD_CREATE_PERMISSION 0x008 +#define NR_METHOD_CHANNEL_BIND 0x009 + +/* define types for a TURN message */ +#define NR_STUN_MSG_ALLOCATE_REQUEST NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_ALLOCATE_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_ALLOCATE, \ + NR_CLASS_ERROR_RESPONSE) +#define NR_STUN_MSG_REFRESH_REQUEST NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_REFRESH_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_REFRESH_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_REFRESH, \ + NR_CLASS_ERROR_RESPONSE) + +#define NR_STUN_MSG_SEND_INDICATION NR_STUN_TYPE(NR_METHOD_SEND, \ + NR_CLASS_INDICATION) +#define NR_STUN_MSG_DATA_INDICATION NR_STUN_TYPE(NR_METHOD_DATA, \ + NR_CLASS_INDICATION) + +#define NR_STUN_MSG_PERMISSION_REQUEST NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_PERMISSION_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_PERMISSION_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CREATE_PERMISSION, \ + NR_CLASS_ERROR_RESPONSE) + +#define NR_STUN_MSG_CHANNEL_BIND_REQUEST NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_REQUEST) +#define NR_STUN_MSG_CHANNEL_BIND_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_RESPONSE) +#define NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE NR_STUN_TYPE(NR_METHOD_CHANNEL_BIND, \ + NR_CLASS_ERROR_RESPONSE) + + +#endif /* USE_TURN */ + + +#define NR_STUN_AUTH_RULE_OPTIONAL (1<<0) +#define NR_STUN_AUTH_RULE_SHORT_TERM (1<<8) +#define NR_STUN_AUTH_RULE_LONG_TERM (1<<9) + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c new file mode 100644 index 0000000000..001b38e7c4 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.c @@ -0,0 +1,611 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#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]; + size_t i; + int r, _status; + size_t len; + + /* First check that the password is ASCII. We are supposed to + SASLprep but we don't support this yet + TODO(ekr@rtfm.com): Add SASLprep for password. + */ + for (i=0; ilen; 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/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h new file mode 100644 index 0000000000..c3f91a87b0 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_build.h @@ -0,0 +1,147 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _stun_build_h +#define _stun_build_h + +#include "stun.h" + +#define NR_STUN_MODE_STUN 1 +#ifdef USE_STUND_0_96 +#define NR_STUN_MODE_STUND_0_96 2 /* backwards compatibility mode */ +#endif /* USE_STUND_0_96 */ +#define NR_STUN_MODE_STUN_NO_AUTH 3 +int nr_stun_form_request_or_indication(int mode, int msg_type, nr_stun_message **msg); + +typedef struct nr_stun_client_stun_binding_request_params_ { + char *username; + Data *password; + char *nonce; + char *realm; +} nr_stun_client_stun_binding_request_params; + +int nr_stun_build_req_lt_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); +int nr_stun_build_req_st_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); +int nr_stun_build_req_no_auth(nr_stun_client_stun_binding_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_stun_keepalive_params_ { +#if defined(WIN32) || defined(__clang__) + // VC++ and clang give error and warning respectively if no members + int dummy; +#endif +} nr_stun_client_stun_keepalive_params; + +int nr_stun_build_keepalive(nr_stun_client_stun_keepalive_params *params, nr_stun_message **msg); + + +#ifdef USE_STUND_0_96 +typedef struct nr_stun_client_stun_binding_request_stund_0_96_params_ { +#ifdef WIN32 // silly VC++ gives error if no members + int dummy; +#endif +} nr_stun_client_stun_binding_request_stund_0_96_params; + +int nr_stun_build_req_stund_0_96(nr_stun_client_stun_binding_request_stund_0_96_params *params, nr_stun_message **msg); +#endif /* USE_STUND_0_96 */ + + +#ifdef USE_ICE +typedef struct nr_stun_client_ice_binding_request_params_ { + char *username; + Data password; + UINT4 priority; + int control; +#define NR_ICE_CONTROLLING 1 +#define NR_ICE_CONTROLLED 2 + UINT8 tiebreaker; +} nr_stun_client_ice_binding_request_params; + +int nr_stun_build_use_candidate(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg); + +int nr_stun_build_req_ice(nr_stun_client_ice_binding_request_params *params, nr_stun_message **msg); +#endif /* USE_ICE */ + + +typedef struct nr_stun_client_auth_params_ { + char authenticate; + char *username; + char *realm; + char *nonce; + Data password; +} nr_stun_client_auth_params; + +#ifdef USE_TURN +typedef struct nr_stun_client_allocate_request_params_ { + UINT4 lifetime_secs; +} nr_stun_client_allocate_request_params; + +int nr_stun_build_allocate_request(nr_stun_client_auth_params *auth, nr_stun_client_allocate_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_refresh_request_params_ { + UINT4 lifetime_secs; +} nr_stun_client_refresh_request_params; + +int nr_stun_build_refresh_request(nr_stun_client_auth_params *auth, nr_stun_client_refresh_request_params *params, nr_stun_message **msg); + + + +typedef struct nr_stun_client_permission_request_params_ { + nr_transport_addr remote_addr; +} nr_stun_client_permission_request_params; + +int nr_stun_build_permission_request(nr_stun_client_auth_params *auth, nr_stun_client_permission_request_params *params, nr_stun_message **msg); + + +typedef struct nr_stun_client_send_indication_params_ { + nr_transport_addr remote_addr; + Data data; +} nr_stun_client_send_indication_params; + +int nr_stun_build_send_indication(nr_stun_client_send_indication_params *params, nr_stun_message **msg); + +typedef struct nr_stun_client_data_indication_params_ { + nr_transport_addr remote_addr; + Data data; +} nr_stun_client_data_indication_params; + +int nr_stun_build_data_indication(nr_stun_client_data_indication_params *params, nr_stun_message **msg); +#endif /* USE_TURN */ + +int nr_stun_form_success_response(nr_stun_message *req, nr_transport_addr *from, Data *password, nr_stun_message *res); +void nr_stun_form_error_response(nr_stun_message *request, nr_stun_message* response, int number, char* msg); +int nr_stun_compute_lt_message_integrity_password(const char *username, const char *realm, + Data *password, Data *hmac_key); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c new file mode 100644 index 0000000000..50b9f74a5b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.c @@ -0,0 +1,888 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include +#include "stun.h" +#include "async_timer.h" +#include "registry.h" +#include "stun_reg.h" +#include "nr_crypto.h" +#include "r_time.h" + +static int nr_stun_client_send_request(nr_stun_client_ctx *ctx); +static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg); +static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password); + +#define NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD 1 +#define NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK 2 + +int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx=0; + char allow_loopback; + int r,_status; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_client_ctx)))) + ABORT(R_NO_MEMORY); + + ctx->state=NR_STUN_CLIENT_STATE_INITTED; + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + ctx->sock=sock; + + nr_socket_getaddr(sock,&ctx->my_addr); + nr_transport_addr_copy(&ctx->peer_addr,peer); + assert(ctx->my_addr.protocol==ctx->peer_addr.protocol); + + if (RTO != 0) { + ctx->rto_ms = RTO; + } else if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) { + ctx->rto_ms = 100; + } + + if (NR_reg_get_double(NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF, &ctx->retransmission_backoff_factor)) + ctx->retransmission_backoff_factor = 2.0; + + if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS, &ctx->maximum_transmits)) + ctx->maximum_transmits = 7; + + if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->maximum_transmits_timeout_ms)) + ctx->maximum_transmits_timeout_ms = 16 * ctx->rto_ms; + + ctx->mapped_addr_check_mask = NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD; + if (NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback) || + !allow_loopback) { + ctx->mapped_addr_check_mask |= NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK; + } + + if (ctx->my_addr.protocol == IPPROTO_TCP) { + /* Because TCP is reliable there is only one final timeout value. + * We store the timeout value for TCP in here, because timeout_ms gets + * reset to 0 in client_reset() which gets called from client_start() */ + ctx->maximum_transmits_timeout_ms = ctx->rto_ms * + pow(ctx->retransmission_backoff_factor, + ctx->maximum_transmits); + ctx->maximum_transmits = 1; + } + + *ctxp=ctx; + + _status=0; + abort: + if(_status){ + nr_stun_client_ctx_destroy(&ctx); + } + return(_status); + } + +static void nr_stun_client_fire_finished_cb(nr_stun_client_ctx *ctx) + { + if (ctx->finished_cb) { + NR_async_cb finished_cb = ctx->finished_cb; + ctx->finished_cb = 0; /* prevent 2nd call */ + /* finished_cb call must be absolutely last thing in function + * because as a side effect this ctx may be operated on in the + * callback */ + finished_cb(0,0,ctx->cb_arg); + } + } + +int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg) + { + int r,_status; + + if (ctx->state != NR_STUN_CLIENT_STATE_INITTED) + ABORT(R_NOT_PERMITTED); + + ctx->mode=mode; + + ctx->state=NR_STUN_CLIENT_STATE_RUNNING; + ctx->finished_cb=finished_cb; + ctx->cb_arg=cb_arg; + + if(mode!=NR_STUN_CLIENT_MODE_KEEPALIVE){ + if(r=nr_stun_client_send_request(ctx)) + ABORT(r); + } + + _status=0; + abort: + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { + nr_stun_client_fire_finished_cb(ctx); + } + + return(_status); + } + + int nr_stun_client_restart(nr_stun_client_ctx* ctx, + const nr_transport_addr* peer_addr) { + int r,_status; + int mode; + NR_async_cb finished_cb; + void *cb_arg; + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + mode = ctx->mode; + finished_cb = ctx->finished_cb; + cb_arg = ctx->cb_arg; + + nr_stun_client_reset(ctx); + nr_transport_addr_copy(&ctx->peer_addr, peer_addr); + + if (r=nr_stun_client_start(ctx, mode, finished_cb, cb_arg)) + ABORT(r); + + _status=0; + abort: + return(_status); + } + +int +nr_stun_client_reset(nr_stun_client_ctx *ctx) +{ + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + nr_stun_message_destroy(&ctx->request); + ctx->request = 0; + + nr_stun_message_destroy(&ctx->response); + ctx->response = 0; + + memset(&ctx->results, 0, sizeof(ctx->results)); + + ctx->mode = 0; + ctx->finished_cb = 0; + ctx->cb_arg = 0; + ctx->request_ct = 0; + ctx->timeout_ms = 0; + + ctx->state = NR_STUN_CLIENT_STATE_INITTED; + + return 0; +} + +static void nr_stun_client_timer_expired_cb(NR_SOCKET s, int b, void *cb_arg) + { + int _status; + nr_stun_client_ctx *ctx=cb_arg; + struct timeval now; + INT8 ms_waited; + + /* Prevent this timer from being cancelled later */ + ctx->timer_handle=0; + + /* Shouldn't happen */ + if(ctx->state==NR_STUN_CLIENT_STATE_CANCELLED) + ABORT(R_REJECTED); + + gettimeofday(&now, 0); + if (r_timeval_diff_ms(&now, &ctx->timer_set, &ms_waited)) { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired",ctx->label); + } + else { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired (after %llu ms)",ctx->label, ms_waited); + } + + if (ctx->request_ct >= ctx->maximum_transmits) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Timed out",ctx->label); + ctx->state=NR_STUN_CLIENT_STATE_TIMED_OUT; + ABORT(R_FAILED); + } + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + // track retransmits for ice telemetry + nr_accumulate_count(&(ctx->retransmit_ct), 1); + + /* as a side effect will reset the timer */ + nr_stun_client_send_request(ctx); + + _status = 0; + abort: + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) { + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + nr_stun_client_fire_finished_cb(ctx); + } + if (_status) { + // cb doesn't return anything, but we should probably log that we aborted + // This also quiets the unused variable warnings. + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Timer expired cb abort with status: %d", + ctx->label, _status); + } + return; + } + +int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx) + { + int r,_status; + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + if (ctx->request_ct > ctx->maximum_transmits) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Too many retransmit attempts",ctx->label); + ABORT(R_FAILED); + } + + /* if there is a scheduled retransimt, get rid of the scheduled retransmit + * and retransmit immediately */ + if (ctx->timer_handle) { + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + + if (r=nr_stun_client_send_request(ctx)) + ABORT(r); + } + + _status=0; + abort: + + return(_status); + } + +static int nr_stun_client_send_request(nr_stun_client_ctx *ctx) + { + int r,_status; + char string[256]; + + if (ctx->state != NR_STUN_CLIENT_STATE_RUNNING) + ABORT(R_NOT_PERMITTED); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Sending check request (my_addr=%s,peer_addr=%s)",ctx->label,ctx->my_addr.as_string,ctx->peer_addr.as_string); + + if (ctx->request == 0) { + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + ctx->params.stun_binding_request.nonce = ctx->nonce; + ctx->params.stun_binding_request.realm = ctx->realm; + assert(0); + ABORT(R_INTERNAL); + /* TODO(ekr@rtfm.com): Need to implement long-term auth for binding + requests */ + if ((r=nr_stun_build_req_lt_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + if ((r=nr_stun_build_req_st_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + if ((r=nr_stun_build_req_no_auth(&ctx->params.stun_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_STUN_CLIENT_MODE_KEEPALIVE: + if ((r=nr_stun_build_keepalive(&ctx->params.stun_keepalive, &ctx->request))) + ABORT(r); + break; +#ifdef USE_STUND_0_96 + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + if ((r=nr_stun_build_req_stund_0_96(&ctx->params.stun_binding_request_stund_0_96, &ctx->request))) + ABORT(r); + break; +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + if ((r=nr_stun_build_use_candidate(&ctx->params.ice_binding_request, &ctx->request))) + ABORT(r); + break; + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + if ((r=nr_stun_build_req_ice(&ctx->params.ice_binding_request, &ctx->request))) + ABORT(r); + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + if ((r=nr_stun_build_allocate_request(&ctx->auth_params, &ctx->params.allocate_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + if ((r=nr_stun_build_refresh_request(&ctx->auth_params, &ctx->params.refresh_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + if ((r=nr_stun_build_permission_request(&ctx->auth_params, &ctx->params.permission_request, &ctx->request))) + ABORT(r); + break; + case NR_TURN_CLIENT_MODE_SEND_INDICATION: + if ((r=nr_stun_build_send_indication(&ctx->params.send_indication, &ctx->request))) + ABORT(r); + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + } + + if (ctx->request->length == 0) { + if ((r=nr_stun_encode_message(ctx->request))) + ABORT(r); + } + + snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Sending to %s ", ctx->label, ctx->peer_addr.as_string); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)ctx->request->buffer, ctx->request->length); + + assert(ctx->my_addr.protocol==ctx->peer_addr.protocol); + + if(r=nr_socket_sendto(ctx->sock, ctx->request->buffer, ctx->request->length, 0, &ctx->peer_addr)) { + if (r != R_WOULDBLOCK) { + ABORT(r); + } + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_socket_sendto blocked, treating as dropped packet",ctx->label); + } + + ctx->request_ct++; + + if (NR_STUN_GET_TYPE_CLASS(ctx->request->header.type) == NR_CLASS_INDICATION) { + /* no need to set the timer because indications don't receive a + * response */ + } + else { + if (ctx->request_ct >= ctx->maximum_transmits) { + /* Reliable transport only get here once. Unreliable get here for + * their final timeout. */ + ctx->timeout_ms += ctx->maximum_transmits_timeout_ms; + } + else if (ctx->timeout_ms) { + /* exponential backoff */ + ctx->timeout_ms *= ctx->retransmission_backoff_factor; + } + else { + /* initial timeout unreliable transports */ + ctx->timeout_ms = ctx->rto_ms; + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Next timer will fire in %u ms",ctx->label, ctx->timeout_ms); + + gettimeofday(&ctx->timer_set, 0); + + assert(ctx->timeout_ms); + NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle); + } + + _status=0; + abort: + if (_status) { + nr_stun_client_failed(ctx); + } + return(_status); + } + +static int nr_stun_client_get_password(void *arg, nr_stun_message *msg, Data **password) +{ + *password = (Data*)arg; + if (!arg) + return(R_NOT_FOUND); + return(0); +} + +int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 check) + { + if((check & NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD) && nr_transport_addr_is_wildcard(addr)) + return(R_BAD_DATA); + + if ((check & NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK) && nr_transport_addr_is_loopback(addr)) + return(R_BAD_DATA); + + return(0); + } + +int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr) + { + int r,_status; + char string[256]; + char *username = 0; + Data *password = 0; + int allow_unauthed_redirect = 0; + nr_stun_message_attribute *attr; + nr_transport_addr *mapped_addr = 0; + int fail_on_error = 0; + UCHAR hmac_key_d[16]; + Data hmac_key; + int compute_lt_key=0; + /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */ + int response_matched=0; + + ATTACH_DATA(hmac_key, hmac_key_d); + + if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) && + (ctx->state != NR_STUN_CLIENT_STATE_WAITING)) + ABORT(R_REJECTED); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Inspecting STUN response (my_addr=%s, peer_addr=%s)",ctx->label,ctx->my_addr.as_string,peer_addr->as_string); + + snprintf(string, sizeof(string)-1, "STUN-CLIENT(%s): Received ", ctx->label); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len); + + /* determine password */ + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + /* If the STUN server responds with an error, give up, since we don't + * want to delay the completion of gathering. */ + fail_on_error = 1; + compute_lt_key = 1; + /* Fall through */ + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + password = ctx->params.stun_binding_request.password; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + /* If the STUN server responds with an error, give up, since we don't + * want to delay the completion of gathering. */ + fail_on_error = 1; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + /* If the STUN server responds with an error, give up, since we don't + * want to delay the completion of gathering. */ + fail_on_error = 1; + break; + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + /* We do not set fail_on_error here. The error might be transient, and + * retrying isn't going to cause a slowdown. */ + password = &ctx->params.ice_binding_request.password; + break; + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + /* We do not set fail_on_error here. The error might be transient, and + * retrying isn't going to cause a slowdown. */ + password = &ctx->params.ice_binding_request.password; + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + /* Do not require mutual auth on redirect responses to Allocate requests. */ + allow_unauthed_redirect = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + fail_on_error = 1; + compute_lt_key = 1; + username = ctx->auth_params.username; + password = &ctx->auth_params.password; + /* do nothing */ + break; + case NR_TURN_CLIENT_MODE_SEND_INDICATION: + /* do nothing -- we just got our DATA-INDICATION */ + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + + if (compute_lt_key) { + if (!ctx->realm || !username) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Long-term auth required but no realm/username specified. Randomizing key"); + /* Fill the key with random bytes to guarantee non-match */ + if (r=nr_crypto_random_bytes(hmac_key_d, sizeof(hmac_key_d))) + ABORT(r); + } + else { + if (r=nr_stun_compute_lt_message_integrity_password(username, ctx->realm, + password, &hmac_key)) + ABORT(r); + } + password = &hmac_key; + } + + if (ctx->response) { + nr_stun_message_destroy(&ctx->response); + } + + /* TODO(bcampen@mozilla.com): Bug 1023619, refactor this. */ + if ((r=nr_stun_message_create2(&ctx->response, msg, len))) + ABORT(r); + + if ((r=nr_stun_decode_message(ctx->response, nr_stun_client_get_password, password))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): error decoding response",ctx->label); + ABORT(r); + } + + /* This will return an error if request and response don't match, + which is how we reject responses that match other contexts. */ + if ((r=nr_stun_receive_message(ctx->request, ctx->response))) { + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Response is not for us",ctx->label); + ABORT(r); + } + + r_log(NR_LOG_STUN,LOG_INFO, + "STUN-CLIENT(%s): Received response; processing",ctx->label); + response_matched=1; + + if (allow_unauthed_redirect && + nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_ERROR_CODE, + &attr) && + (attr->u.error_code.number / 100 == 3)) { + password = 0; + } + +/* TODO: !nn! currently using password!=0 to mean that auth is required, + * TODO: !nn! but we should probably pass that in explicitly via the + * TODO: !nn! usage (ctx->mode?) */ + if (password) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_NONCE, 0)) { + if ((r=nr_stun_receive_response_long_term_auth(ctx->response, ctx))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): long term auth failed",ctx->label); + ABORT(r); + } + } + else { + if ((r=nr_stun_receive_response_short_term_auth(ctx->response))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): short term auth failed",ctx->label); + ABORT(r); + } + } + } + + if (NR_STUN_GET_TYPE_CLASS(ctx->response->header.type) == NR_CLASS_RESPONSE) { + if ((r=nr_stun_process_success_response(ctx->response))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_process_success_response failed",ctx->label); + ABORT(r); + } + } + else { + if (fail_on_error) { + ctx->state = NR_STUN_CLIENT_STATE_FAILED; + } + /* Note: most times we call process_error_response, we get r != 0. + + However, if the error is to be discarded, we get r == 0, smash + the error code, and just keep going. + */ + if ((r=nr_stun_process_error_response(ctx->response, &ctx->error_code))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_process_error_response failed",ctx->label); + ABORT(r); + } + else { + ctx->error_code = 0xffff; + /* drop the error on the floor */ + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): processed error response",ctx->label); + ABORT(R_FAILED); + } + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Successfully parsed mode=%d",ctx->label,ctx->mode); + +/* TODO: !nn! this should be moved to individual message receive/processing sections */ + switch (ctx->mode) { + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH: + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) { + /* Compensate for a bug in Google's STUN servers where they always respond with MAPPED-ADDRESS */ + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS but MAPPED-ADDRESS. Falling back (though server is wrong).", ctx->label); + } + else { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS or MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + + case NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, 0) && ! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response_stund_0_96.mapped_addr; + break; + +#ifdef USE_ICE + case NR_ICE_CLIENT_MODE_BINDING_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; + case NR_ICE_CLIENT_MODE_USE_CANDIDATE: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + mapped_addr = &ctx->results.stun_binding_response.mapped_addr; + break; +#endif /* USE_ICE */ + +#ifdef USE_TURN + case NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-MAPPED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_RELAY_ADDRESS, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No XOR-RELAYED-ADDRESS",ctx->label); + ABORT(R_BAD_DATA); + } + + if ((r=nr_stun_transport_addr_check(&attr->u.relay_address.unmasked, + ctx->mapped_addr_check_mask))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_stun_transport_addr_check failed",ctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_copy( + &ctx->results.allocate_response.relay_addr, + &attr->u.relay_address.unmasked))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label); + ABORT(r); + } + + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No LIFETIME",ctx->label); + ABORT(R_BAD_DATA); + } + ctx->results.allocate_response.lifetime_secs=attr->u.lifetime_secs; + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received relay address: %s", ctx->label, ctx->results.allocate_response.relay_addr.as_string); + + mapped_addr = &ctx->results.allocate_response.mapped_addr; + + break; + case NR_TURN_CLIENT_MODE_REFRESH_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + if (!nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_LIFETIME, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No LIFETIME",ctx->label); + ABORT(R_BAD_DATA); + } + ctx->results.refresh_response.lifetime_secs=attr->u.lifetime_secs; + break; + case NR_TURN_CLIENT_MODE_PERMISSION_REQUEST: + if (! nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No MESSAGE-INTEGRITY",ctx->label); + ABORT(R_BAD_DATA); + } + break; +#endif /* USE_TURN */ + + default: + assert(0); + ABORT(R_FAILED); + break; + } + + /* make sure we have the most up-to-date address from this peer */ + if (nr_transport_addr_cmp(&ctx->peer_addr, peer_addr, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(NR_LOG_STUN,LOG_INFO,"STUN-CLIENT(%s): Peer moved from %s to %s", ctx->label, ctx->peer_addr.as_string, peer_addr->as_string); + nr_transport_addr_copy(&ctx->peer_addr, peer_addr); + } + + if (mapped_addr) { + if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, &attr)) { + if ((r=nr_stun_transport_addr_check(&attr->u.xor_mapped_address.unmasked, + ctx->mapped_addr_check_mask))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): XOR-MAPPED-ADDRESS is bogus",ctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.xor_mapped_address.unmasked))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label); + ABORT(r); + } + } + else if (nr_stun_message_has_attribute(ctx->response, NR_STUN_ATTR_MAPPED_ADDRESS, &attr)) { + if ((r=nr_stun_transport_addr_check(&attr->u.mapped_address, + ctx->mapped_addr_check_mask))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): MAPPED-ADDRESS is bogus",ctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_copy(mapped_addr, &attr->u.mapped_address))) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): nr_transport_addr_copy failed",ctx->label); + ABORT(r); + } + } + else { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): No mapped address!",ctx->label); + ABORT(R_BAD_DATA); + } + + // STUN doesn't distinguish protocol in mapped address, therefore + // assign used protocol from peer_addr + if (mapped_addr->protocol!=peer_addr->protocol){ + mapped_addr->protocol=peer_addr->protocol; + nr_transport_addr_fmt_addr_string(mapped_addr); + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Received mapped address: %s", ctx->label, mapped_addr->as_string); + } + + ctx->state=NR_STUN_CLIENT_STATE_DONE; + + _status=0; + abort: + if(_status && response_matched){ + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-CLIENT(%s): Error processing response: %s, stun error code %d.", ctx->label, nr_strerror(_status), (int)ctx->error_code); + } + + if ((ctx->state != NR_STUN_CLIENT_STATE_RUNNING) && + (ctx->state != NR_STUN_CLIENT_STATE_WAITING)) { + /* Cancel the timer firing */ + if (ctx->timer_handle) { + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle = 0; + } + + nr_stun_client_fire_finished_cb(ctx); + } + + return(_status); + } + +int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp) + { + nr_stun_client_ctx *ctx; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + *ctxp=0; + + nr_stun_client_reset(ctx); + + RFREE(ctx->nonce); + RFREE(ctx->realm); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + + +int nr_stun_client_cancel(nr_stun_client_ctx *ctx) + { + /* Cancel the timer firing */ + if (ctx->timer_handle){ + NR_async_timer_cancel(ctx->timer_handle); + ctx->timer_handle=0; + } + + /* Mark cancelled so we ignore any returned messsages */ + ctx->state=NR_STUN_CLIENT_STATE_CANCELLED; + return(0); +} + +int nr_stun_client_wait(nr_stun_client_ctx *ctx) + { + nr_stun_client_cancel(ctx); + ctx->state=NR_STUN_CLIENT_STATE_WAITING; + + ctx->request_ct = ctx->maximum_transmits; + ctx->timeout_ms = ctx->maximum_transmits_timeout_ms; + NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle); + + return(0); + } + +int nr_stun_client_failed(nr_stun_client_ctx *ctx) + { + nr_stun_client_cancel(ctx); + ctx->state=NR_STUN_CLIENT_STATE_FAILED; + nr_stun_client_fire_finished_cb(ctx); + return(0); + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h new file mode 100644 index 0000000000..0cc9045e2a --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_client_ctx.h @@ -0,0 +1,200 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_client_ctx_h +#define _stun_client_ctx_h + +/* forward declaration */ +typedef struct nr_stun_client_ctx_ nr_stun_client_ctx; + +#include "stun.h" + +/* Checklist for adding new STUN transaction types + + 1. Add new method type in stun.h (NR_METHOD_*) + 2. Add new MSGs in stun.h (NR_STUN_MSG_*) + 3. Add new messages to stun_util.c:nr_stun_msg_type + 4. Add new request type to stun_build.h + 4. Add new message builder to stun_build.c + 5. Add new response type to stun_client_ctx.h + 6. Add new arm to stun_client_ctx.c:nr_stun_client_send_request + 7. Add new arms to nr_stun_client_process_response + 8. Add new arms to stun_hint.c:nr_is_stun_message +*/ + + + + +typedef union nr_stun_client_params_ { + + nr_stun_client_stun_binding_request_params stun_binding_request; + nr_stun_client_stun_keepalive_params stun_keepalive; +#ifdef USE_STUND_0_96 + nr_stun_client_stun_binding_request_stund_0_96_params stun_binding_request_stund_0_96; +#endif /* USE_STUND_0_96 */ + +#ifdef USE_ICE + nr_stun_client_ice_binding_request_params ice_binding_request; +#endif /* USE_ICE */ + +#ifdef USE_TURN + nr_stun_client_allocate_request_params allocate_request; + nr_stun_client_refresh_request_params refresh_request; + nr_stun_client_permission_request_params permission_request; + nr_stun_client_send_indication_params send_indication; +#endif /* USE_TURN */ + +} nr_stun_client_params; + +typedef struct nr_stun_client_stun_binding_response_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_stun_binding_response_results; + +typedef struct nr_stun_client_stun_binding_response_stund_0_96_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_stun_binding_response_stund_0_96_results; + +#ifdef USE_ICE +typedef struct nr_stun_client_ice_use_candidate_results_ { +#ifdef WIN32 // silly VC++ gives error if no members + int dummy; +#endif +} nr_stun_client_ice_use_candidate_results; + +typedef struct nr_stun_client_ice_binding_response_results_ { + nr_transport_addr mapped_addr; +} nr_stun_client_ice_binding_response_results; +#endif /* USE_ICE */ + +#ifdef USE_TURN +typedef struct nr_stun_client_allocate_response_results_ { + nr_transport_addr relay_addr; + nr_transport_addr mapped_addr; + UINT4 lifetime_secs; +} nr_stun_client_allocate_response_results; + +typedef struct nr_stun_client_refresh_response_results_ { + UINT4 lifetime_secs; +} nr_stun_client_refresh_response_results; + +typedef struct nr_stun_client_permission_response_results_ { + UINT4 lifetime_secs; +} nr_stun_client_permission_response_results; + +#endif /* USE_TURN */ + +typedef union nr_stun_client_results_ { + nr_stun_client_stun_binding_response_results stun_binding_response; + nr_stun_client_stun_binding_response_stund_0_96_results stun_binding_response_stund_0_96; + +#ifdef USE_ICE + nr_stun_client_ice_use_candidate_results ice_use_candidate; + nr_stun_client_ice_binding_response_results ice_binding_response; +#endif /* USE_ICE */ + +#ifdef USE_TURN + nr_stun_client_allocate_response_results allocate_response; + nr_stun_client_refresh_response_results refresh_response; +#endif /* USE_TURN */ +} nr_stun_client_results; + +struct nr_stun_client_ctx_ { + int state; +#define NR_STUN_CLIENT_STATE_INITTED 0 +#define NR_STUN_CLIENT_STATE_RUNNING 1 +#define NR_STUN_CLIENT_STATE_DONE 2 +#define NR_STUN_CLIENT_STATE_FAILED 3 +#define NR_STUN_CLIENT_STATE_TIMED_OUT 4 +#define NR_STUN_CLIENT_STATE_CANCELLED 5 +#define NR_STUN_CLIENT_STATE_WAITING 6 + + int mode; +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_SHORT_TERM_AUTH 1 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_LONG_TERM_AUTH 2 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_NO_AUTH 3 +#define NR_STUN_CLIENT_MODE_KEEPALIVE 4 +#define NR_STUN_CLIENT_MODE_BINDING_REQUEST_STUND_0_96 5 +#ifdef USE_ICE +#define NR_ICE_CLIENT_MODE_USE_CANDIDATE 10 +#define NR_ICE_CLIENT_MODE_BINDING_REQUEST 11 +#endif /* USE_ICE */ +#ifdef USE_TURN +#define NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST 20 +#define NR_TURN_CLIENT_MODE_REFRESH_REQUEST 21 +#define NR_TURN_CLIENT_MODE_SEND_INDICATION 22 +#define NR_TURN_CLIENT_MODE_DATA_INDICATION 24 +#define NR_TURN_CLIENT_MODE_PERMISSION_REQUEST 25 +#endif /* USE_TURN */ + + char *label; + nr_transport_addr my_addr; + nr_transport_addr peer_addr; + nr_socket *sock; + nr_stun_client_auth_params auth_params; + nr_stun_client_params params; + nr_stun_client_results results; + char *nonce; + char *realm; + void *timer_handle; + UINT2 request_ct; + UINT2 retransmit_ct; + UINT4 rto_ms; /* retransmission time out */ + double retransmission_backoff_factor; + UINT4 maximum_transmits; + UINT4 maximum_transmits_timeout_ms; + UINT4 mapped_addr_check_mask; /* What checks to run on mapped addresses */ + int timeout_ms; + struct timeval timer_set; + NR_async_cb finished_cb; + void *cb_arg; + nr_stun_message *request; + nr_stun_message *response; + UINT2 error_code; +}; + +int nr_stun_client_ctx_create(char *label, nr_socket *sock, nr_transport_addr *peer, UINT4 RTO, nr_stun_client_ctx **ctxp); +int nr_stun_client_start(nr_stun_client_ctx *ctx, int mode, NR_async_cb finished_cb, void *cb_arg); +int nr_stun_client_restart(nr_stun_client_ctx* ctx, + const nr_transport_addr* peer_addr); +int nr_stun_client_force_retransmit(nr_stun_client_ctx *ctx); +int nr_stun_client_reset(nr_stun_client_ctx *ctx); +int nr_stun_client_ctx_destroy(nr_stun_client_ctx **ctxp); +int nr_stun_transport_addr_check(nr_transport_addr* addr, UINT4 mask); +int nr_stun_client_process_response(nr_stun_client_ctx *ctx, UCHAR *msg, int len, nr_transport_addr *peer_addr); +int nr_stun_client_cancel(nr_stun_client_ctx *ctx); +int nr_stun_client_wait(nr_stun_client_ctx *ctx); +int nr_stun_client_failed(nr_stun_client_ctx *ctx); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c new file mode 100644 index 0000000000..ae748a667b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c @@ -0,0 +1,1550 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#ifdef WIN32 +#include +#include +#include +#include +#else /* UNIX */ +#include +#endif /* end UNIX */ +#include +#include + +#include "nr_api.h" +#include "stun.h" +#include "byteorder.h" +#include "r_crc32.h" +#include "nr_crypto.h" + +#define NR_STUN_IPV4_FAMILY 0x01 +#define NR_STUN_IPV6_FAMILY 0x02 + +#define SKIP_ATTRIBUTE_DECODE -1 + +static int nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info); + +static int nr_stun_fix_attribute_ordering(nr_stun_message *msg); + +static int nr_stun_encode_htons(UINT2 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode_htonl(UINT4 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode_htonll(UINT8 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode(UCHAR *data, size_t length, size_t buflen, UCHAR *buf, size_t *offset); + +static int nr_stun_decode_htons(UCHAR *buf, size_t buflen, size_t *offset, UINT2 *data); +static int nr_stun_decode_htonl(UCHAR *buf, size_t buflen, size_t *offset, UINT4 *data); +static int nr_stun_decode_htonll(UCHAR *buf, size_t buflen, size_t *offset, UINT8 *data); +static int nr_stun_decode(size_t length, UCHAR *buf, size_t buflen, size_t *offset, UCHAR *data); + +static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t max_chars); + +static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data); + + +int +nr_stun_encode_htons(UINT2 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT2 d = htons(data); + + if (*offset + sizeof(d) >= buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd >= %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonl(UINT4 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT4 d = htonl(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonll(UINT8 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT8 d = nr_htonll(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode(UCHAR *data, size_t length, size_t buflen, UCHAR *buf, size_t *offset) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], data, length); + *offset += length; + + return 0; +} + + +int +nr_stun_decode_htons(UCHAR *buf, size_t buflen, size_t *offset, UINT2 *data) +{ + UINT2 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htons(d); + + return 0; +} + +int +nr_stun_decode_htonl(UCHAR *buf, size_t buflen, size_t *offset, UINT4 *data) +{ + UINT4 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htonl(d); + + return 0; +} + +int +nr_stun_decode_htonll(UCHAR *buf, size_t buflen, size_t *offset, UINT8 *data) +{ + UINT8 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = nr_htonll(d); + + return 0; +} + +int +nr_stun_decode(size_t length, UCHAR *buf, size_t buflen, size_t *offset, UCHAR *data) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(data, &buf[*offset], length); + *offset += length; + + return 0; +} + +/** + * The argument must be a non-null pointer to a zero-terminated string. + * + * If the argument is valid UTF-8, returns the number of code points in the + * string excluding the zero-terminator. + * + * If the argument is invalid UTF-8, returns a lower bound for the number of + * code points in the string. (If UTF-8 error handling was performed on the + * string, new REPLACEMENT CHARACTER code points could be introduced in + * a way that would increase the total number of code points compared to + * what this function counts.) + */ +size_t +nr_count_utf8_code_points_without_validation(const char *s) { + size_t nchars = 0; + char c; + while ((c = *s)) { + if ((c & 0xC0) != 0x80) { + ++nchars; + } + ++s; + } + return nchars; +} + +int +nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t max_chars) +{ + int _status; + char *s = data; + size_t nchars; + + if (len > max_bytes) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %d bytes", attr_info->name, len); + ABORT(R_FAILED); + } + + nchars = nr_count_utf8_code_points_without_validation(s); + if (nchars > max_chars) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %zd characters", attr_info->name, nchars); + ABORT(R_FAILED); + } + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + int r,_status; + nr_stun_attr_error_code *ec = data; + + if (ec->number < 300 || ec->number > 699) + ABORT(R_FAILED); + + if ((r=nr_stun_attr_string_illegal(attr_info, strlen(ec->reason), ec->reason, NR_STUN_MAX_ERROR_CODE_REASON_BYTES, NR_STUN_MAX_ERROR_CODE_REASON_CHARS))) + ABORT(r); + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_NONCE_BYTES, NR_STUN_MAX_NONCE_CHARS); +} + +int +nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_REALM_BYTES, NR_STUN_MAX_REALM_CHARS); +} + +int +nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_SERVER_BYTES, NR_STUN_MAX_SERVER_CHARS); +} + +int +nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_USERNAME_BYTES, -1); +} + +static int +nr_stun_attr_codec_UCHAR_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UCHAR*)data); + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + UINT4 tmp = *((UCHAR *)data); + tmp <<= 24; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(tmp , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + UINT4 tmp; + + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, &tmp)) + return R_FAILED; + + *((UCHAR *)data) = (tmp >> 24) & 0xff; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UCHAR = { + "UCHAR", + nr_stun_attr_codec_UCHAR_print, + nr_stun_attr_codec_UCHAR_encode, + nr_stun_attr_codec_UCHAR_decode +}; + +static int +nr_stun_attr_codec_UINT4_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UINT4*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT4_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(*(UINT4*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT4_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, (UINT4*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT4 = { + "UINT4", + nr_stun_attr_codec_UINT4_print, + nr_stun_attr_codec_UINT4_encode, + nr_stun_attr_codec_UINT4_decode +}; + +static int +nr_stun_attr_codec_UINT8_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %llu", msg, attr_info->name, *(UINT8*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT8_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT8) , buflen, buf, &offset) + || nr_stun_encode_htonll(*(UINT8*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT8_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != sizeof(UINT8)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonll(buf, buflen, &offset, (UINT8*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT8 = { + "UINT8", + nr_stun_attr_codec_UINT8_print, + nr_stun_attr_codec_UINT8_encode, + nr_stun_attr_codec_UINT8_decode +}; + +static int +nr_stun_attr_codec_addr_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", msg, attr_info->name, ((nr_transport_addr*)data)->as_string); + return 0; +} + +static int +nr_stun_attr_codec_addr_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int r,_status; + int start = offset; + nr_transport_addr *addr = data; + UCHAR pad = '\0'; + UCHAR family; + + if ((r=nr_stun_encode_htons(attr_info->type, buflen, buf, &offset))) + ABORT(r); + + switch (addr->ip_version) { + case NR_IPV4: + family = NR_STUN_IPV4_FAMILY; + if (nr_stun_encode_htons(8 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr4.sin_port), buflen, buf, &offset) + || nr_stun_encode_htonl(ntohl(addr->u.addr4.sin_addr.s_addr), buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + case NR_IPV6: + family = NR_STUN_IPV6_FAMILY; + if (nr_stun_encode_htons(20 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr6.sin6_port), buflen, buf, &offset) + || nr_stun_encode(addr->u.addr6.sin6_addr.s6_addr, 16, buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_addr_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + UCHAR pad; + UCHAR family; + UINT2 port; + UINT4 addr4; + struct in6_addr addr6; + nr_transport_addr *result = data; + + if (nr_stun_decode(1, buf, buflen, &offset, &pad) + || nr_stun_decode(1, buf, buflen, &offset, &family)) + ABORT(R_FAILED); + + switch (family) { + case NR_STUN_IPV4_FAMILY: + if (attrlen != 8) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode_htonl(buf, buflen, &offset, &addr4)) + ABORT(R_FAILED); + + if (nr_ip4_port_to_transport_addr(addr4, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + case NR_STUN_IPV6_FAMILY: + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode(16, buf, buflen, &offset, addr6.s6_addr)) + ABORT(R_FAILED); + + if (nr_ip6_port_to_transport_addr(&addr6, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + default: + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal address family: %d", family); + ABORT(R_FAILED); + break; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_addr = { + "addr", + nr_stun_attr_codec_addr_print, + nr_stun_attr_codec_addr_encode, + nr_stun_attr_codec_addr_decode +}; + +static int +nr_stun_attr_codec_data_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_data *d = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)d->data, d->length); + return 0; +} + +static int +nr_stun_attr_codec_data_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_data *d = data; + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(d->length , buflen, buf, &offset) + || nr_stun_encode(d->data, d->length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_data_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_data *result = data; + + /* -1 because it is going to be null terminated just to be safe */ + if (attrlen >= (sizeof(result->data) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too much data: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->data)) + ABORT(R_FAILED); + + result->length = attrlen; + result->data[attrlen] = '\0'; /* just to be nice */ + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_data = { + "data", + nr_stun_attr_codec_data_print, + nr_stun_attr_codec_data_encode, + nr_stun_attr_codec_data_decode +}; + +static int +nr_stun_attr_codec_error_code_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_error_code *error_code = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %d %s", + msg, attr_info->name, error_code->number, + error_code->reason); + return 0; +} + +static int +nr_stun_attr_codec_error_code_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_error_code *error_code = data; + int start = offset; + int length = strlen(error_code->reason); + UCHAR pad[2] = { 0 }; + UCHAR class = error_code->number / 100; + UCHAR number = error_code->number % 100; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(4 + length , buflen, buf, &offset) + || nr_stun_encode(pad, 2 , buflen, buf, &offset) + || nr_stun_encode(&class, 1 , buflen, buf, &offset) + || nr_stun_encode(&number, 1 , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)error_code->reason, length, buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_error_code_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_error_code *result = data; + UCHAR pad[2]; + UCHAR class; + UCHAR number; + size_t size_reason; + + if (nr_stun_decode(2, buf, buflen, &offset, pad) + || nr_stun_decode(1, buf, buflen, &offset, &class) + || nr_stun_decode(1, buf, buflen, &offset, &number)) + ABORT(R_FAILED); + + result->number = (class * 100) + number; + + size_reason = attrlen - 4; + + /* -1 because the string will be null terminated */ + if (size_reason > (sizeof(result->reason) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Reason is too large, truncating"); + /* don't fail, but instead truncate the reason */ + size_reason = sizeof(result->reason) - 1; + } + + if (nr_stun_decode(size_reason, buf, buflen, &offset, (UCHAR*)result->reason)) + ABORT(R_FAILED); + result->reason[size_reason] = '\0'; + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_error_code = { + "error_code", + nr_stun_attr_codec_error_code_print, + nr_stun_attr_codec_error_code_encode, + nr_stun_attr_codec_error_code_decode +}; + +static int +nr_stun_attr_codec_fingerprint_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_fingerprint *fingerprint = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %08x", msg, attr_info->name, fingerprint->checksum); + return 0; +} + +static int +nr_stun_attr_codec_fingerprint_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + UINT4 checksum; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + header->length = ntohs(header->length); + header->length += 8; /* Fingerprint */ + header->length = htons(header->length); + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + return R_FAILED; + } + + fingerprint->checksum = checksum ^ 0x5354554e; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum); + + fingerprint->valid = 1; + return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int r,_status; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + size_t length; + UINT4 checksum; + + if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum))) + ABORT(r); + + offset -= 4; /* rewind to before the length and type fields */ + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + length = offset; /* right before FINGERPRINT */ + length -= sizeof(*header); /* remove header length */ + length += 8; /* add length of Fingerprint */ + header->length = htons(length); + + /* make sure FINGERPRINT is final attribute in message */ + if (length + sizeof(*header) != buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Fingerprint is not final attribute in message"); + ABORT(R_FAILED); + } + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + ABORT(R_FAILED); + } + + fingerprint->valid = (fingerprint->checksum == (checksum ^ 0x5354554e)); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", (checksum ^ 0x5354554e)); + if (! fingerprint->valid) + r_log(NR_LOG_STUN, LOG_WARNING, "Invalid FINGERPRINT %08x", fingerprint->checksum); + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_fingerprint = { + "fingerprint", + nr_stun_attr_codec_fingerprint_print, + nr_stun_attr_codec_fingerprint_encode, + nr_stun_attr_codec_fingerprint_decode +}; + +static int +nr_stun_attr_codec_flag_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: on", msg, attr_info->name); + return 0; +} + +static int +nr_stun_attr_codec_flag_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(0 , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_flag_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal flag length: %d", attrlen); + return R_FAILED; + } + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_flag = { + "flag", + nr_stun_attr_codec_flag_print, + nr_stun_attr_codec_flag_encode, + nr_stun_attr_codec_flag_decode +}; + +static int +nr_stun_attr_codec_message_integrity_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_message_integrity *integrity = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)integrity->hash, sizeof(integrity->hash)); + return 0; +} + +static int +nr_stun_compute_message_integrity(UCHAR *buf, int offset, UCHAR *password, int passwordlen, UCHAR *computedHMAC) +{ + int r,_status; + UINT2 hold; + UINT2 length; + nr_stun_message_header *header; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computing MESSAGE-INTEGRITY"); + + header = (nr_stun_message_header*)buf; + hold = header->length; + + /* adjust the length of the message */ + length = offset; + length -= sizeof(*header); + length += 24; /* for MESSAGE-INTEGRITY attribute */ + header->length = htons(length); + + if ((r=nr_crypto_hmac_sha1((UCHAR*)password, passwordlen, + buf, offset, computedHMAC))) + ABORT(r); + + r_dump(NR_LOG_STUN, LOG_DEBUG, "Computed MESSAGE-INTEGRITY ", (char*)computedHMAC, 20); + + _status=0; + abort: + header->length = hold; + return _status; +} + +static int +nr_stun_attr_codec_message_integrity_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + nr_stun_attr_message_integrity *integrity = data; + + if (nr_stun_compute_message_integrity(buf, offset, integrity->password, integrity->passwordlen, integrity->hash)) + return R_FAILED; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(integrity->hash) , buflen, buf, &offset) + || nr_stun_encode(integrity->hash, sizeof(integrity->hash) , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_message_integrity_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + int start; + nr_stun_attr_message_integrity *result = data; + UCHAR computedHMAC[20]; + + result->valid = 0; + + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s must be 20 bytes, not %d", attr_info->name, attrlen); + ABORT(R_FAILED); + } + + start = offset - 4; /* rewind to before the length and type fields */ + if (start < 0) + ABORT(R_INTERNAL); + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->hash)) + ABORT(R_FAILED); + + if (result->unknown_user) { + result->valid = 0; + } + else { + if (nr_stun_compute_message_integrity(buf, start, result->password, result->passwordlen, computedHMAC)) + ABORT(R_FAILED); + + assert(sizeof(computedHMAC) == sizeof(result->hash)); + + result->valid = (memcmp(computedHMAC, result->hash, 20) == 0); + } + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_message_integrity = { + "message_integrity", + nr_stun_attr_codec_message_integrity_print, + nr_stun_attr_codec_message_integrity_encode, + nr_stun_attr_codec_message_integrity_decode +}; + +static int +nr_stun_attr_codec_noop_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + return SKIP_ATTRIBUTE_DECODE; +} + +nr_stun_attr_codec nr_stun_attr_codec_noop = { + "NOOP", + 0, /* ignore, never print these attributes */ + 0, /* ignore, never encode these attributes */ + nr_stun_attr_codec_noop_decode +}; + +static int +nr_stun_attr_codec_quoted_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_quoted_string_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ +//TODO: !nn! syntax check, conversion if not quoted already? +//We'll just restrict this in the API -- EKR + return nr_stun_attr_codec_string.encode(attr_info, data, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_quoted_string_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ +//TODO: !nn! I don't see any need to unquote this but we may +//find one later -- EKR + return nr_stun_attr_codec_string.decode(attr_info, attrlen, buf, offset, buflen, data); +} + +nr_stun_attr_codec nr_stun_attr_codec_quoted_string = { + "quoted_string", + nr_stun_attr_codec_quoted_string_print, + nr_stun_attr_codec_quoted_string_encode, + nr_stun_attr_codec_quoted_string_decode +}; + +static int +nr_stun_attr_codec_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_string_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + char *str = data; + int length = strlen(str); + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)str, length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_string_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + char *result = data; + + /* actual enforcement of the specific string size happens elsewhere */ + if (attrlen >= NR_STUN_MAX_STRING_SIZE) { + r_log(NR_LOG_STUN, LOG_WARNING, "String is too large: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, (UCHAR*)result)) + ABORT(R_FAILED); + result[attrlen] = '\0'; /* just to be nice */ + + if (strlen(result) != attrlen) { + /* stund 0.96 sends a final null in the Server attribute, so + * only error if the null appears anywhere else in a string */ + if (strlen(result) != attrlen-1) { + r_log(NR_LOG_STUN, LOG_WARNING, "Error in string: %zd/%d", strlen(result), attrlen); + ABORT(R_FAILED); + } + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_string = { + "string", + nr_stun_attr_codec_string_print, + nr_stun_attr_codec_string_encode, + nr_stun_attr_codec_string_decode +}; + +static int +nr_stun_attr_codec_unknown_attributes_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_unknown_attributes *unknown_attributes = data; + char type[9]; + char str[64 + (NR_STUN_MAX_UNKNOWN_ATTRIBUTES * sizeof(type))]; + int i; + + snprintf(str, sizeof(str), "%s %s:", msg, attr_info->name); + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + snprintf(type, sizeof(type), "%s 0x%04x", ((i>0)?",":""), unknown_attributes->attribute[i]); + strlcat(str, type, sizeof(str)); + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "%s", str); + return 0; +} + +static int +nr_stun_attr_codec_unknown_attributes_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int _status; + int start = offset; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int length = (2 * unknown_attributes->num_attributes); + int i; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_FAILED); + } + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset)) + ABORT(R_FAILED); + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + if (nr_stun_encode_htons(unknown_attributes->attribute[i], buflen, buf, &offset)) + ABORT(R_FAILED); + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_unknown_attributes_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int i; + UINT2 *a; + + if ((attrlen % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute is illegal size: %d", attrlen); + ABORT(R_REJECTED); + } + + unknown_attributes->num_attributes = attrlen / 2; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_REJECTED); + } + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + a = &(unknown_attributes->attribute[i]); + if (nr_stun_decode_htons(buf, buflen, &offset, a)) + return R_FAILED; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes = { + "unknown_attributes", + nr_stun_attr_codec_unknown_attributes_print, + nr_stun_attr_codec_unknown_attributes_encode, + nr_stun_attr_codec_unknown_attributes_decode +}; + +static int +nr_stun_attr_codec_xor_mapped_address_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s (unmasked) %s (masked)", + msg, attr_info->name, + xor_mapped_address->unmasked.as_string, + xor_mapped_address->masked.as_string); + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->unmasked, &xor_mapped_address->masked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + if (nr_stun_attr_codec_addr.encode(attr_info, &xor_mapped_address->masked, offset, buflen, buf, attrlen)) + return R_FAILED; + + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int r,_status; + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + if ((r=nr_stun_attr_codec_addr.decode(attr_info, attrlen, buf, offset, buflen, &xor_mapped_address->masked))) + ABORT(r); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->masked, &xor_mapped_address->unmasked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + 0, /* never encode this type */ + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address = { + "xor_peer_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +#define NR_ADD_STUN_ATTRIBUTE(type, name, codec, illegal) \ + { (type), (name), &(codec), illegal }, + +#define NR_ADD_STUN_ATTRIBUTE_IGNORE(type, name) \ + { (type), (name), &nr_stun_attr_codec_noop, 0 }, + + +static nr_stun_attr_info attrs[] = { + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ALTERNATE_SERVER, "ALTERNATE-SERVER", nr_stun_attr_codec_addr, 0) +#ifdef USE_STUND_0_96 + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_CHANGE_REQUEST, "CHANGE-REQUEST", nr_stun_attr_codec_UINT4, 0) +#endif + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ERROR_CODE, "ERROR-CODE", nr_stun_attr_codec_error_code, nr_stun_attr_error_code_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_FINGERPRINT, "FINGERPRINT", nr_stun_attr_codec_fingerprint, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MAPPED_ADDRESS, "MAPPED-ADDRESS", nr_stun_attr_codec_addr, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MESSAGE_INTEGRITY, "MESSAGE-INTEGRITY", nr_stun_attr_codec_message_integrity, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_NONCE, "NONCE", nr_stun_attr_codec_quoted_string, nr_stun_attr_nonce_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REALM, "REALM", nr_stun_attr_codec_quoted_string, nr_stun_attr_realm_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_SERVER, "SERVER", nr_stun_attr_codec_string, nr_stun_attr_server_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, "UNKNOWN-ATTRIBUTES", nr_stun_attr_codec_unknown_attributes, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USERNAME, "USERNAME", nr_stun_attr_codec_string, nr_stun_attr_username_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_MAPPED_ADDRESS, "XOR-MAPPED-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + +#ifdef USE_ICE + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLED, "ICE-CONTROLLED", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLING, "ICE-CONTROLLING", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_PRIORITY, "PRIORITY", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USE_CANDIDATE, "USE-CANDIDATE", nr_stun_attr_codec_flag, 0) +#endif + +#ifdef USE_TURN + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_DATA, "DATA", nr_stun_attr_codec_data, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_LIFETIME, "LIFETIME", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_RELAY_ADDRESS, "XOR-RELAY-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_PEER_ADDRESS, "XOR-PEER-ADDRESS", nr_stun_attr_codec_xor_peer_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REQUESTED_TRANSPORT, "REQUESTED-TRANSPORT", nr_stun_attr_codec_UCHAR, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_BANDWIDTH, "BANDWIDTH", nr_stun_attr_codec_UINT4, 0) +#endif /* USE_TURN */ + + /* for backwards compatibilty */ + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS, "Old XOR-MAPPED-ADDRESS", nr_stun_attr_codec_old_xor_mapped_address, 0) +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_RESPONSE_ADDRESS, "RESPONSE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_SOURCE_ADDRESS, "SOURCE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_CHANGED_ADDRESS, "CHANGED-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_PASSWORD, "PASSWORD") +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ +}; + + +int +nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info) +{ + int _status; + size_t i; + + *info = 0; + for (i = 0; i < sizeof(attrs)/sizeof(*attrs); ++i) { + if (type == attrs[i].type) { + *info = &attrs[i]; + break; + } + } + + if (*info == 0) + ABORT(R_NOT_FOUND); + + _status=0; + abort: + return(_status); +} + +int +nr_stun_fix_attribute_ordering(nr_stun_message *msg) +{ + nr_stun_message_attribute *message_integrity; + nr_stun_message_attribute *fingerprint; + + /* 2nd to the last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &message_integrity)) { + TAILQ_REMOVE(&msg->attributes, message_integrity, entry); + TAILQ_INSERT_TAIL(&msg->attributes, message_integrity, entry); + } + + /* last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &fingerprint)) { + TAILQ_REMOVE(&msg->attributes, fingerprint, entry); + TAILQ_INSERT_TAIL(&msg->attributes, fingerprint, entry); + } + + return 0; +} + +// Since this sanity check is only a collection of assert statements and those +// assert statements are compiled out in non-debug builds, undef SANITY_CHECKS +// so we can avoid the warning that padding_bytes is never used in opt builds. +#ifdef NDEBUG +#undef SANITY_CHECKS +#endif + +#ifdef SANITY_CHECKS +static void sanity_check_encoding_stuff(nr_stun_message *msg) +{ + nr_stun_message_attribute *attr = 0; + int padding_bytes; + int l; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Starting to sanity check encoding"); + + l = 0; + TAILQ_FOREACH(attr, &msg->attributes, entry) { + padding_bytes = 0; + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + } + assert(attr->length == (attr->encoding_length - (4 + padding_bytes))); + assert(((void*)attr->encoding) == (msg->buffer + 20 + l)); + l += attr->encoding_length; + assert((l % 4) == 0); + } + assert(l == msg->header.length); +} +#endif /* SANITY_CHECKS */ + + +int +nr_stun_encode_message(nr_stun_message *msg) +{ + int r,_status; + size_t length_offset; + size_t length_offset_hold; + nr_stun_attr_info *attr_info; + nr_stun_message_attribute *attr; + int padding_bytes; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoding STUN message"); + + nr_stun_fix_attribute_ordering(msg); + + msg->name = nr_stun_msg_type(msg->header.type); + msg->length = 0; + msg->header.length = 0; + + if ((r=nr_stun_encode_htons(msg->header.type, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: 0x%03x", msg->header.type); + + /* grab the offset to be used later to re-write the header length field */ + length_offset_hold = msg->length; + + if ((r=nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + + if ((r=nr_stun_encode_htonl(msg->header.magic_cookie, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Cookie: %08x", msg->header.magic_cookie); + + if ((r=nr_stun_encode((UCHAR*)(&msg->header.id), sizeof(msg->header.id), sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Encoded ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + TAILQ_FOREACH(attr, &msg->attributes, entry) { + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unrecognized attribute: 0x%04x", attr->type); + ABORT(R_INTERNAL); + } + + attr->name = attr_info->name; + attr->type_name = attr_info->codec->name; + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[msg->length]; + + if (attr_info->codec->encode != 0) { + if ((r=attr_info->codec->encode(attr_info, &attr->u, msg->length, sizeof(msg->buffer), msg->buffer, &attr->encoding_length))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to encode %s", attr_info->name); + ABORT(r); + } + + msg->length += attr->encoding_length; + attr->length = attr->encoding_length - 4; /* -4 for type and length fields */ + + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) + ABORT(r); + } + + attr_info->codec->print(attr_info, "Encoded", &attr->u); + + if ((attr->length % 4) == 0) { + padding_bytes = 0; + } + else { + padding_bytes = 4 - (attr->length % 4); + nr_stun_encode((UCHAR*)"\0\0\0\0", padding_bytes, sizeof(msg->buffer), msg->buffer, &msg->length); + attr->encoding_length += padding_bytes; + } + + msg->header.length += attr->encoding_length; + length_offset = length_offset_hold; + (void)nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &length_offset); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing encode function for attribute: %s", attr_info->name); + } + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Length: %d", msg->header.length); + + assert(msg->length < NR_STUN_MAX_MESSAGE_SIZE); + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; +abort: + return _status; +} + +int +nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg) +{ + int r,_status; + int offset; + int size; + int padding_bytes; + nr_stun_message_attribute *attr; + nr_stun_attr_info *attr_info; + Data *password; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsing STUN message of %d bytes", msg->length); + + if (!TAILQ_EMPTY(&msg->attributes)) + ABORT(R_BAD_ARGS); + + if (sizeof(nr_stun_message_header) > msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Message too small"); + ABORT(R_FAILED); + } + + memcpy(&msg->header, msg->buffer, sizeof(msg->header)); + msg->header.type = ntohs(msg->header.type); + msg->header.length = ntohs(msg->header.length); + msg->header.magic_cookie = ntohl(msg->header.magic_cookie); + + msg->name = nr_stun_msg_type(msg->header.type); + + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: 0x%03x", msg->header.type); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Length: %d", msg->header.length); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Cookie: %08x", msg->header.magic_cookie); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Parsed ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + if (msg->header.length + sizeof(msg->header) != msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Inconsistent message header length: %d/%d", + msg->header.length, msg->length); + ABORT(R_FAILED); + } + + size = msg->header.length; + + if ((size % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message size: %d", msg->header.length); + ABORT(R_FAILED); + } + + offset = sizeof(msg->header); + + while (size > 0) { + r_log(NR_LOG_STUN, LOG_DEBUG, "size = %d", size); + + if (size < 4) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message length: %d", size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_message_attribute_create(msg, &attr))) + ABORT(R_NO_MEMORY); + + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[offset]; + attr->type = ntohs(attr->encoding->type); + attr->length = ntohs(attr->encoding->length); + attr->encoding_length = attr->length + 4; + + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + attr->encoding_length += padding_bytes; + } + + if ((attr->encoding_length) > (size_t)size) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute length larger than remaining message size: %d/%d", attr->encoding_length, size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + if (attr->type <= 0x7FFF) + ++msg->comprehension_required_unknown_attributes; + else + ++msg->comprehension_optional_unknown_attributes; + r_log(NR_LOG_STUN, LOG_INFO, "Unrecognized attribute: 0x%04x", attr->type); + } + else { + attr_info->name = attr_info->name; + attr->type_name = attr_info->codec->name; + + if (attr->type == NR_STUN_ATTR_MESSAGE_INTEGRITY) { + if (get_password && get_password(arg, msg, &password) == 0) { + if (password->len > sizeof(attr->u.message_integrity.password)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Password too long: %d bytes", password->len); + ABORT(R_FAILED); + } + + memcpy(attr->u.message_integrity.password, password->data, password->len); + attr->u.message_integrity.passwordlen = password->len; + } + else { + /* set to user "not found" */ + attr->u.message_integrity.unknown_user = 1; + } + } + else if (attr->type == NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS) { + attr->type = NR_STUN_ATTR_XOR_MAPPED_ADDRESS; + r_log(NR_LOG_STUN, LOG_INFO, "Translating obsolete XOR-MAPPED-ADDRESS type"); + } + + if ((r=attr_info->codec->decode(attr_info, attr->length, msg->buffer, offset+4, msg->length, &attr->u))) { + if (r == SKIP_ATTRIBUTE_DECODE) { + r_log(NR_LOG_STUN, LOG_INFO, "Skipping %s", attr_info->name); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to parse %s", attr_info->name); + } + + attr->invalid = 1; + } + else { + attr_info->codec->print(attr_info, "Parsed", &attr->u); + +#ifdef USE_STUN_PEDANTIC + r_log(NR_LOG_STUN, LOG_DEBUG, "Before pedantic attr_info checks"); + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Failed pedantic attr_info checks"); + ABORT(r); + } + } + r_log(NR_LOG_STUN, LOG_DEBUG, "After pedantic attr_info checks"); +#endif /* USE_STUN_PEDANTIC */ + } + } + + offset += attr->encoding_length; + size -= attr->encoding_length; + } + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; + abort: + return _status; +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h new file mode 100644 index 0000000000..4e4ff60e0c --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.h @@ -0,0 +1,78 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_codec_h +#define _stun_codec_h + +#include "stun_msg.h" + +typedef struct nr_stun_attr_info_ nr_stun_attr_info; + +typedef struct nr_stun_attr_codec_ { + char *name; + int (*print)(nr_stun_attr_info *attr_info, char *msg, void *data); + int (*encode)(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen); + int (*decode)(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data); +} nr_stun_attr_codec; + +struct nr_stun_attr_info_ { + UINT2 type; + char *name; + nr_stun_attr_codec *codec; + int (*illegal)(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +}; + +extern nr_stun_attr_codec nr_stun_attr_codec_UINT4; +extern nr_stun_attr_codec nr_stun_attr_codec_UINT8; +extern nr_stun_attr_codec nr_stun_attr_codec_addr; +extern nr_stun_attr_codec nr_stun_attr_codec_bytes; +extern nr_stun_attr_codec nr_stun_attr_codec_data; +extern nr_stun_attr_codec nr_stun_attr_codec_error_code; +extern nr_stun_attr_codec nr_stun_attr_codec_fingerprint; +extern nr_stun_attr_codec nr_stun_attr_codec_flag; +extern nr_stun_attr_codec nr_stun_attr_codec_message_integrity; +extern nr_stun_attr_codec nr_stun_attr_codec_noop; +extern nr_stun_attr_codec nr_stun_attr_codec_quoted_string; +extern nr_stun_attr_codec nr_stun_attr_codec_string; +extern nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes; +extern nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address; +extern nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address; +extern nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address; + +size_t nr_count_utf8_code_points_without_validation(const char *s); +int nr_stun_encode_message(nr_stun_message *msg); +int nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c new file mode 100644 index 0000000000..8f118e8942 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.c @@ -0,0 +1,245 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#ifdef WIN32 +#include +#include +#include +#include +#else /* UNIX */ +#include +#endif /* end UNIX */ +#include + +#include "stun.h" + + +/* returns 0 if it's not a STUN message + * 1 if it's likely to be a STUN message + * 2 if it's super likely to be a STUN message + * 3 if it really is a STUN message */ +int +nr_is_stun_message(UCHAR *buf, size_t len) +{ + const UINT4 cookie = htonl(NR_STUN_MAGIC_COOKIE); + const UINT4 cookie2 = htonl(NR_STUN_MAGIC_COOKIE2); +#if 0 + nr_stun_message msg; +#endif + UINT2 type; + nr_stun_encoded_attribute* attr; + unsigned int attrLen; + int atrType; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if ((buf[0] & (0x80|0x40)) != 0) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + switch (type) { + case NR_STUN_MSG_BINDING_REQUEST: + case NR_STUN_MSG_BINDING_INDICATION: + case NR_STUN_MSG_BINDING_RESPONSE: + case NR_STUN_MSG_BINDING_ERROR_RESPONSE: + +#ifdef USE_TURN + case NR_STUN_MSG_ALLOCATE_REQUEST: + case NR_STUN_MSG_ALLOCATE_RESPONSE: + case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE: + case NR_STUN_MSG_REFRESH_REQUEST: + case NR_STUN_MSG_REFRESH_RESPONSE: + case NR_STUN_MSG_REFRESH_ERROR_RESPONSE: + case NR_STUN_MSG_PERMISSION_REQUEST: + case NR_STUN_MSG_PERMISSION_RESPONSE: + case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE: + case NR_STUN_MSG_CHANNEL_BIND_REQUEST: + case NR_STUN_MSG_CHANNEL_BIND_RESPONSE: + case NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE: + case NR_STUN_MSG_SEND_INDICATION: + case NR_STUN_MSG_DATA_INDICATION: +#ifdef NR_STUN_MSG_CONNECT_REQUEST + case NR_STUN_MSG_CONNECT_REQUEST: +#endif +#ifdef NR_STUN_MSG_CONNECT_RESPONSE + case NR_STUN_MSG_CONNECT_RESPONSE: +#endif +#ifdef NR_STUN_MSG_CONNECT_ERROR_RESPONSE + case NR_STUN_MSG_CONNECT_ERROR_RESPONSE: +#endif +#ifdef NR_STUN_MSG_CONNECT_STATUS_INDICATION + case NR_STUN_MSG_CONNECT_STATUS_INDICATION: +#endif +#endif /* USE_TURN */ + + /* ok so far, continue */ + break; + default: + return 0; + break; + } + + if (!memcmp(&cookie2, &buf[4], sizeof(UINT4))) { + /* return here because if it's an old-style message then there will + * not be a fingerprint in the message */ + return 1; + } + + if (memcmp(&cookie, &buf[4], sizeof(UINT4))) + return 0; + + /* the magic cookie was right, so it's pretty darn likely that what we've + * got here is a STUN message */ + + attr = (nr_stun_encoded_attribute*)(buf + (len - 8)); + attrLen = ntohs(attr->length); + atrType = ntohs(attr->type); + + if (atrType != NR_STUN_ATTR_FINGERPRINT || attrLen != 4) + return 1; + + /* the fingerprint is in the right place and looks sane, so we can be quite + * sure we've got a STUN message */ + +#if 0 +/* nevermind this check ... there's a reasonable chance that a NAT has modified + * the message (and thus the fingerprint check will fail), but it's still an + * otherwise-perfectly-good STUN message, so skip the check since we're going + * to return "true" whether the check succeeds or fails */ + + if (nr_stun_parse_attr_UINT4(buf + (len - 4), attrLen, &msg.fingerprint)) + return 2; + + + if (nr_stun_compute_fingerprint(buf, len - 8, &computedFingerprint)) + return 2; + + if (msg.fingerprint.number != computedFingerprint) + return 2; + + /* and the fingerprint is good, so it's gotta be a STUN message */ +#endif + + return 3; +} + +int +nr_is_stun_request_message(UCHAR *buf, size_t len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_REQUEST; +} + +int +nr_is_stun_indication_message(UCHAR *buf, size_t len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_INDICATION; +} + +int +nr_is_stun_response_message(UCHAR *buf, size_t len) +{ + UINT2 type; + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (!nr_is_stun_message(buf, len)) + return 0; + + memcpy(&type, buf, 2); + type = ntohs(type); + + return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_RESPONSE + || NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_ERROR_RESPONSE; +} + +int +nr_has_stun_cookie(UCHAR *buf, size_t len) +{ + static UINT4 cookie; + + cookie = htonl(NR_STUN_MAGIC_COOKIE); + + if (sizeof(nr_stun_message_header) > len) + return 0; + + if (memcmp(&cookie, &buf[4], sizeof(UINT4))) + return 0; + + return 1; +} + +int +nr_stun_message_length(UCHAR *buf, int buf_len, int *msg_len) +{ + nr_stun_message_header *hdr; + + if (!nr_is_stun_message(buf, buf_len)) + return(R_BAD_DATA); + + hdr = (nr_stun_message_header *)buf; + + *msg_len = ntohs(hdr->length); + + return(0); +} + + + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h new file mode 100644 index 0000000000..c2badc1d2b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_hint.h @@ -0,0 +1,44 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _stun_hint_h +#define _stun_hint_h + +int nr_is_stun_message(UCHAR *buf, size_t len); +int nr_is_stun_request_message(UCHAR *buf, size_t len); +int nr_is_stun_response_message(UCHAR *buf, size_t len); +int nr_is_stun_indication_message(UCHAR *buf, size_t len); +int nr_has_stun_cookie(UCHAR *buf, size_t len); +int nr_stun_message_length(UCHAR *buf, int len, int *length); + +#endif diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c new file mode 100644 index 0000000000..7e01686109 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.c @@ -0,0 +1,364 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#ifdef WIN32 +#include +#include +#include +#include +#else /* UNIX */ +#include +#endif /* end UNIX */ +#include + +#include "stun.h" + + +int +nr_stun_message_create(nr_stun_message **msg) +{ + int _status; + nr_stun_message *m = 0; + + m = RCALLOC(sizeof(*m)); + if (!m) + ABORT(R_NO_MEMORY); + + TAILQ_INIT(&m->attributes); + + *msg = m; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, size_t length) +{ + int r,_status; + nr_stun_message *m = 0; + + if (length > sizeof(m->buffer)) { + ABORT(R_BAD_DATA); + } + + if ((r=nr_stun_message_create(&m))) + ABORT(r); + + memcpy(m->buffer, buffer, length); + m->length = length; + + *msg = m; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_destroy(nr_stun_message **msg) +{ + int _status; + nr_stun_message_attribute_head *attrs; + nr_stun_message_attribute *attr; + + if (msg && *msg) { + attrs = &(*msg)->attributes; + while (!TAILQ_EMPTY(attrs)) { + attr = TAILQ_FIRST(attrs); + nr_stun_message_attribute_destroy(*msg, &attr); + } + + RFREE(*msg); + + *msg = 0; + } + + _status=0; +/* abort: */ + return(_status); +} + +int +nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr) +{ + int _status; + nr_stun_message_attribute *a = 0; + + a = RCALLOC(sizeof(*a)); + if (!a) + ABORT(R_NO_MEMORY); + + TAILQ_INSERT_TAIL(&msg->attributes, a, entry); + + *attr = a; + + _status=0; + abort: + return(_status); +} + +int +nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr) +{ + int _status; + nr_stun_message_attribute *a = 0; + + if (attr && *attr) { + a = *attr; + TAILQ_REMOVE(&msg->attributes, a, entry); + + RFREE(a); + + *attr = 0; + } + + _status=0; +/* abort: */ + return(_status); +} + +int +nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute) +{ + nr_stun_message_attribute *attr = 0; + nr_stun_message_get_attribute(msg, type, 0, &attr); + + if (attribute) + *attribute = attr; + + return attr ? 1 : 0; +} + +int +nr_stun_message_get_attribute(nr_stun_message *msg, UINT2 type, UINT2 index, nr_stun_message_attribute **attribute) +{ + nr_stun_message_attribute *attr; + TAILQ_FOREACH(attr, &msg->attributes, entry) { + if (attr->type == type && !attr->invalid) { + if (!index) { + *attribute = attr; + return 0; + } + --index; + } + } + *attribute = 0; + return R_NOT_FOUND; +} + +#define NR_STUN_MESSAGE_ADD_ATTRIBUTE(__type, __code) \ + { \ + int r,_status; \ + nr_stun_message_attribute *attr = 0; \ + if ((r=nr_stun_message_attribute_create(msg, &attr))) \ + ABORT(r); \ + attr->type = (__type); \ + { __code } \ + _status=0; \ + abort: \ + if (_status){ \ + nr_stun_message_attribute_destroy(msg, &attr); \ + } \ + return(_status); \ + } + + +int +nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ALTERNATE_SERVER, + { + if ((r=nr_transport_addr_copy(&attr->u.alternate_server, alternate_server))) + ABORT(r); + } +) + +int +nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ERROR_CODE, + { + attr->u.error_code.number = number; + (void)strlcpy(attr->u.error_code.reason, reason, sizeof(attr->u.error_code.reason)); + } +) + +int +nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_FINGERPRINT, + {} +) + +int +nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_MESSAGE_INTEGRITY, + { + if (sizeof(attr->u.message_integrity.password) < password->len) + ABORT(R_BAD_DATA); + + memcpy(attr->u.message_integrity.password, password->data, password->len); + attr->u.message_integrity.passwordlen = password->len; + } +) + +int +nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_NONCE, + { (void)strlcpy(attr->u.nonce, nonce, sizeof(attr->u.nonce)); } +) + +int +nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_REALM, + { (void)strlcpy(attr->u.realm, realm, sizeof(attr->u.realm)); } +) + +int +nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_SERVER, + { (void)strlcpy(attr->u.server_name, server_name, sizeof(attr->u.server_name)); } +) + +int +nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, + { memcpy(&attr->u.unknown_attributes, unknown_attributes, sizeof(attr->u.unknown_attributes)); } +) + +int +nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_USERNAME, + { (void)strlcpy(attr->u.username, username, sizeof(attr->u.username)); } +) + +int +nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR protocol) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_REQUESTED_TRANSPORT, + { attr->u.requested_transport = protocol; } +) + +int +nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_XOR_MAPPED_ADDRESS, + { + if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, mapped_address))) + ABORT(r); + } +) + +int +nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_XOR_PEER_ADDRESS, + { + if ((r=nr_transport_addr_copy(&attr->u.xor_mapped_address.unmasked, peer_address))) + ABORT(r); + } +) + +#ifdef USE_ICE +int +nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ICE_CONTROLLED, + { attr->u.ice_controlled = ice_controlled; } +) + +int +nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_ICE_CONTROLLING, + { attr->u.ice_controlling = ice_controlling; } +) + +int +nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_PRIORITY, + { attr->u.priority = priority; } +) + +int +nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_USE_CANDIDATE, + {} +) +#endif /* USE_ICE */ + +#ifdef USE_TURN +int +nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length) + +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_DATA, + { + if (length > NR_STUN_MAX_MESSAGE_SIZE) + ABORT(R_BAD_ARGS); + + memcpy(attr->u.data.data, data, length); + attr->u.data.length=length; + } +) + +int +nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_LIFETIME, + { attr->u.lifetime_secs = lifetime_secs; } +) + +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 +int +nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request) +NR_STUN_MESSAGE_ADD_ATTRIBUTE( + NR_STUN_ATTR_OLD_CHANGE_REQUEST, + { attr->u.change_request = change_request; } +) +#endif /* USE_STUND_0_96 */ + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h new file mode 100644 index 0000000000..ffd68d3eee --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_msg.h @@ -0,0 +1,208 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_msg_h +#define _stun_msg_h + +#include "csi_platform.h" +#include "nr_api.h" +#include "transport_addr.h" + +#define NR_STUN_MAX_USERNAME_BYTES 513 +#define NR_STUN_MAX_ERROR_CODE_REASON_BYTES 763 +#define NR_STUN_MAX_ERROR_CODE_REASON_CHARS 128 +#define NR_STUN_MAX_REALM_BYTES 763 +#define NR_STUN_MAX_REALM_CHARS 128 +#define NR_STUN_MAX_NONCE_BYTES 763 +#define NR_STUN_MAX_NONCE_CHARS 128 +#define NR_STUN_MAX_SERVER_BYTES 763 +#define NR_STUN_MAX_SERVER_CHARS 128 +#define NR_STUN_MAX_STRING_SIZE 763 /* any possible string */ +#define NR_STUN_MAX_UNKNOWN_ATTRIBUTES 16 +#define NR_STUN_MAX_MESSAGE_SIZE 2048 + +#define NR_STUN_MAGIC_COOKIE 0x2112A442 +#define NR_STUN_MAGIC_COOKIE2 0xc5cb4e1d /* used recognize old stun messages */ + +typedef struct { UCHAR octet[12]; } UINT12; + +typedef struct nr_stun_attr_error_code_ { + UINT2 number; + char reason[NR_STUN_MAX_ERROR_CODE_REASON_BYTES+1]; /* +1 for \0 */ +} nr_stun_attr_error_code; + +typedef struct nr_stun_attr_fingerprint_ { + UINT4 checksum; + int valid; +} nr_stun_attr_fingerprint; + +typedef struct nr_stun_attr_message_integrity_ { + UCHAR hash[20]; + int unknown_user; + UCHAR password[1024]; + int passwordlen; + int valid; +} nr_stun_attr_message_integrity; + +typedef struct nr_stun_attr_unknown_attributes_ { + UINT2 attribute[NR_STUN_MAX_UNKNOWN_ATTRIBUTES]; + int num_attributes; +} nr_stun_attr_unknown_attributes; + +typedef struct nr_stun_attr_xor_mapped_address_ { + nr_transport_addr masked; + nr_transport_addr unmasked; +} nr_stun_attr_xor_mapped_address; + +typedef struct nr_stun_attr_data_ { + UCHAR data[NR_STUN_MAX_MESSAGE_SIZE]; + size_t length; +} nr_stun_attr_data; + + +typedef struct nr_stun_encoded_attribute_ { + UINT2 type; + UINT2 length; + UCHAR value[NR_STUN_MAX_MESSAGE_SIZE]; +} nr_stun_encoded_attribute; + +typedef struct nr_stun_message_attribute_ { + UINT2 type; + UINT2 length; + union { + nr_transport_addr address; + nr_transport_addr alternate_server; + nr_stun_attr_error_code error_code; + nr_stun_attr_fingerprint fingerprint; + nr_transport_addr mapped_address; + nr_stun_attr_message_integrity message_integrity; + char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */ + char realm[NR_STUN_MAX_REALM_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_xor_mapped_address relay_address; + char server_name[NR_STUN_MAX_SERVER_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_unknown_attributes unknown_attributes; + char username[NR_STUN_MAX_USERNAME_BYTES+1]; /* +1 for \0 */ + nr_stun_attr_xor_mapped_address xor_mapped_address; + +#ifdef USE_ICE + UINT4 priority; + UINT8 ice_controlled; + UINT8 ice_controlling; +#endif /* USE_ICE */ + +#ifdef USE_TURN + UINT4 lifetime_secs; + nr_transport_addr remote_address; + UCHAR requested_transport; + nr_stun_attr_data data; +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 + UINT4 change_request; +#endif /* USE_STUND_0_96 */ + + /* make sure there's enough room here to place any possible + * attribute */ + UCHAR largest_possible_attribute[NR_STUN_MAX_MESSAGE_SIZE]; + } u; + nr_stun_encoded_attribute *encoding; + size_t encoding_length; + char *name; + char *type_name; + int invalid; + TAILQ_ENTRY(nr_stun_message_attribute_) entry; +} nr_stun_message_attribute; + +typedef TAILQ_HEAD(nr_stun_message_attribute_head_,nr_stun_message_attribute_) nr_stun_message_attribute_head; + +typedef struct nr_stun_message_header_ { + UINT2 type; + UINT2 length; + UINT4 magic_cookie; + UINT12 id; +} nr_stun_message_header; + +typedef struct nr_stun_message_ { + char *name; + UCHAR buffer[NR_STUN_MAX_MESSAGE_SIZE]; + size_t length; + nr_stun_message_header header; + int comprehension_required_unknown_attributes; + int comprehension_optional_unknown_attributes; + nr_stun_message_attribute_head attributes; +} nr_stun_message; + +int nr_stun_message_create(nr_stun_message **msg); +int nr_stun_message_create2(nr_stun_message **msg, UCHAR *buffer, size_t length); +int nr_stun_message_destroy(nr_stun_message **msg); + +int nr_stun_message_attribute_create(nr_stun_message *msg, nr_stun_message_attribute **attr); +int nr_stun_message_attribute_destroy(nr_stun_message *msg, nr_stun_message_attribute **attr); + +int nr_stun_message_has_attribute(nr_stun_message *msg, UINT2 type, nr_stun_message_attribute **attribute); + +int nr_stun_message_get_attribute(nr_stun_message *msg, UINT2 type, UINT2 index, nr_stun_message_attribute **attribute); + +int nr_stun_message_add_alternate_server_attribute(nr_stun_message *msg, nr_transport_addr *alternate_server); +int nr_stun_message_add_error_code_attribute(nr_stun_message *msg, UINT2 number, char *reason); +int nr_stun_message_add_fingerprint_attribute(nr_stun_message *msg); +int nr_stun_message_add_message_integrity_attribute(nr_stun_message *msg, Data *password); +int nr_stun_message_add_nonce_attribute(nr_stun_message *msg, char *nonce); +int nr_stun_message_add_realm_attribute(nr_stun_message *msg, char *realm); +int nr_stun_message_add_server_attribute(nr_stun_message *msg, char *server_name); +int nr_stun_message_add_unknown_attributes_attribute(nr_stun_message *msg, nr_stun_attr_unknown_attributes *unknown_attributes); +int nr_stun_message_add_username_attribute(nr_stun_message *msg, char *username); +int nr_stun_message_add_xor_mapped_address_attribute(nr_stun_message *msg, nr_transport_addr *mapped_address); + +#ifdef USE_ICE +int nr_stun_message_add_ice_controlled_attribute(nr_stun_message *msg, UINT8 ice_controlled); +int nr_stun_message_add_ice_controlling_attribute(nr_stun_message *msg, UINT8 ice_controlling); +int nr_stun_message_add_priority_attribute(nr_stun_message *msg, UINT4 priority); +int nr_stun_message_add_use_candidate_attribute(nr_stun_message *msg); +#endif /* USE_ICE */ + +#ifdef USE_TURN +int nr_stun_message_add_data_attribute(nr_stun_message *msg, UCHAR *data, int length); +int nr_stun_message_add_lifetime_attribute(nr_stun_message *msg, UINT4 lifetime_secs); +int nr_stun_message_add_requested_transport_attribute(nr_stun_message *msg, UCHAR transport); +int +nr_stun_message_add_xor_peer_address_attribute(nr_stun_message *msg, nr_transport_addr *peer_address); +#endif /* USE_TURN */ + +#ifdef USE_STUND_0_96 +int nr_stun_message_add_change_request_attribute(nr_stun_message *msg, UINT4 change_request); +#endif /* USE_STUND_0_96 */ + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c new file mode 100644 index 0000000000..13366e265d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.c @@ -0,0 +1,554 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#ifdef WIN32 +#include +#include +#include +#include +#else /* UNIX */ +#include +#endif /* end UNIX */ +#include + +#include "stun.h" +#include "stun_reg.h" +#include "registry.h" + +static int +nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res); + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3 */ +int +nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg) +{ + int _status; + nr_stun_message_attribute *attr; + +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* if this message was generated by an RFC 3489 impementation, + * the call to nr_is_stun_message will fail, so skip that + * check and puke elsewhere if the message can't be decoded */ + if (msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE + || msg->header.magic_cookie == NR_STUN_MAGIC_COOKIE2) { +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + if (!nr_is_stun_message(msg->buffer, msg->length)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Not a STUN message"); + ABORT(R_REJECTED); + } +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + } +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + + if (req == 0) { + if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_REQUEST) { + r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message type: %03x", msg->header.type); + ABORT(R_REJECTED); + } + } + else { + if (NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_RESPONSE + && NR_STUN_GET_TYPE_CLASS(msg->header.type) != NR_CLASS_ERROR_RESPONSE) { + r_log(NR_LOG_STUN,LOG_WARNING,"Illegal message class: %03x", msg->header.type); + ABORT(R_REJECTED); + } + + if (NR_STUN_GET_TYPE_METHOD(req->header.type) != NR_STUN_GET_TYPE_METHOD(msg->header.type)) { + r_log(NR_LOG_STUN,LOG_WARNING,"Inconsistent message method: %03x expected %03x", msg->header.type, req->header.type); + ABORT(R_REJECTED); + } + + if (nr_stun_different_transaction(msg->buffer, msg->length, req)) { + r_log(NR_LOG_STUN, LOG_DEBUG, "Unrecognized STUN transaction"); + ABORT(R_REJECTED); + } + } + + switch (msg->header.magic_cookie) { + case NR_STUN_MAGIC_COOKIE: + /* basically draft-ietf-behave-rfc3489bis-10.txt S 6 rules */ + + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &attr) + && !attr->u.fingerprint.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Invalid fingerprint"); + ABORT(R_REJECTED); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + + default: +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ +#else +#ifdef NDEBUG + /* in deployment builds we should always see a recognized magic cookie */ + r_log(NR_LOG_STUN, LOG_WARNING, "Missing Magic Cookie"); + ABORT(R_REJECTED); +#else + /* ignore this condition because sometimes we like to pretend we're + * a server talking to old clients and their messages don't contain + * a magic cookie at all but rather the magic cookie field is part + * of their ID and therefore random */ +#endif /* NDEBUG */ +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ + break; + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.1 */ +int +nr_stun_process_request(nr_stun_message *req, nr_stun_message *res) +{ + int _status; +#ifdef USE_STUN_PEDANTIC + int r; + nr_stun_attr_unknown_attributes unknown_attributes = { { 0 } }; + nr_stun_message_attribute *attr; + + if (req->comprehension_required_unknown_attributes > 0) { + nr_stun_form_error_response(req, res, 420, "Unknown Attributes"); + r_log(NR_LOG_STUN, LOG_WARNING, "Request contains comprehension required but unknown attributes"); + + TAILQ_FOREACH(attr, &req->attributes, entry) { + if (attr->name == 0) { + /* unrecognized attribute */ + + /* should never happen, but truncate if it ever were to occur */ + if (unknown_attributes.num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) + break; + + unknown_attributes.attribute[unknown_attributes.num_attributes++] = attr->type; + } + } + + assert(req->comprehension_required_unknown_attributes + req->comprehension_optional_unknown_attributes == unknown_attributes.num_attributes); + + if ((r=nr_stun_message_add_unknown_attributes_attribute(res, &unknown_attributes))) + ABORT(R_ALREADY); + + ABORT(R_ALREADY); + } +#endif /* USE_STUN_PEDANTIC */ + + _status=0; +#ifdef USE_STUN_PEDANTIC + abort: +#endif /* USE_STUN_PEDANTIC */ + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.2 */ +int +nr_stun_process_indication(nr_stun_message *ind) +{ + int _status; +#ifdef USE_STUN_PEDANTIC + + if (ind->comprehension_required_unknown_attributes > 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Indication contains comprehension required but unknown attributes"); + ABORT(R_REJECTED); + } +#endif /* USE_STUN_PEDANTIC */ + + _status=0; +#ifdef USE_STUN_PEDANTIC + abort: +#endif /* USE_STUN_PEDANTIC */ + return _status; +} + +/* RFC5389 S 7.3.3, except that we *also* allow a MAPPED_ADDRESS + to compensate for a bug in Google's STUN server where it + always returns MAPPED_ADDRESS. + + Mozilla bug: 888274. + */ +int +nr_stun_process_success_response(nr_stun_message *res) +{ + int _status; + +#ifdef USE_STUN_PEDANTIC + if (res->comprehension_required_unknown_attributes > 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Response contains comprehension required but unknown attributes"); + ABORT(R_REJECTED); + } +#endif /* USE_STUN_PEDANTIC */ + + if (NR_STUN_GET_TYPE_METHOD(res->header.type) == NR_METHOD_BINDING) { + if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_XOR_MAPPED_ADDRESS, 0) && + ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_MAPPED_ADDRESS, 0)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing XOR-MAPPED-ADDRESS and MAPPED_ADDRESS"); + ABORT(R_REJECTED); + } + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 7.3.4 */ +int +nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code) +{ + int _status; + nr_stun_message_attribute *attr; + + if (res->comprehension_required_unknown_attributes > 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Error response contains comprehension required but unknown attributes"); + ABORT(R_REJECTED); + } + + if (! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, &attr)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing ERROR-CODE"); + ABORT(R_REJECTED); + } + + *error_code = attr->u.error_code.number; + + switch (attr->u.error_code.number / 100) { + case 3: + /* We do not treat STUN/300 as retryable. The TURN Allocate handling + * code will reset the ctx if appropriate. */ + ABORT(R_REJECTED); + break; + + case 4: + /* If the error code is 400 through 499, the client declares the + * transaction failed; in the case of 420 (Unknown Attribute), the + * response should contain a UNKNOWN-ATTRIBUTES attribute that gives + * additional information. */ + if (attr->u.error_code.number == 420) + ABORT(R_REJECTED); + + /* it may be possible to restart given the info that was received in + * this response, so retry */ + ABORT(R_RETRY); + break; + + case 5: + /* If the error code is 500 through 599, the client MAY resend the + * request; clients that do so MUST limit the number of times they do + * this. */ + /* let the retransmit mechanism handle resending the request */ + break; + + default: + ABORT(R_REJECTED); + break; + } + + /* the spec says: "The client then does any processing specified by the authentication + * mechanism (see Section 10). This may result in a new transaction + * attempt." -- but this is handled already elsewhere, so needn't be repeated + * in this function */ + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.2 */ +int +nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg, + nr_stun_message *res) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (msg->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + nr_stun_form_error_response(msg, res, 400, "Missing MESSAGE-INTEGRITY"); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, 0)) { + nr_stun_form_error_response(msg, res, 400, "Missing USERNAME"); + ABORT(R_ALREADY); + } + + if (attr->u.message_integrity.unknown_user) { + nr_stun_form_error_response(msg, res, 401, "Unrecognized USERNAME"); + ABORT(R_ALREADY); + } + + if (!attr->u.message_integrity.valid) { + nr_stun_form_error_response(msg, res, 401, "Bad MESSAGE-INTEGRITY"); + ABORT(R_ALREADY); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.1.3 */ +int +nr_stun_receive_response_short_term_auth(nr_stun_message *res) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (res->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + + if (!attr->u.message_integrity.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + +static int +nr_stun_add_realm_and_nonce(int new_nonce, nr_stun_server_client *clnt, nr_stun_message *res) +{ + int r,_status; + char *realm = 0; + char *nonce; + UINT2 size; + + if ((r=NR_reg_alloc_string(NR_STUN_REG_PREF_SERVER_REALM, &realm))) + ABORT(r); + + if ((r=nr_stun_message_add_realm_attribute(res, realm))) + ABORT(r); + + if (clnt) { + if (strlen(clnt->nonce) < 1) + new_nonce = 1; + + if (new_nonce) { + if (NR_reg_get_uint2(NR_STUN_REG_PREF_SERVER_NONCE_SIZE, &size)) + size = 48; + + if (size > (sizeof(clnt->nonce) - 1)) + size = sizeof(clnt->nonce) - 1; + + nr_random_alphanum(clnt->nonce, size); + clnt->nonce[size] = '\0'; + } + + nonce = clnt->nonce; + } + else { + /* user is not known, so use a bogus nonce since there's no way to + * store a good nonce with the client-specific data -- this nonce + * will be recognized as stale if the client attempts another + * request */ + nonce = "STALE"; + } + + if ((r=nr_stun_message_add_nonce_attribute(res, nonce))) + ABORT(r); + + _status=0; + abort: +#ifdef USE_TURN +assert(_status == 0); /* TODO: !nn! cleanup after I reimplmement TURN */ +#endif + RFREE(realm); + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.1 - 10.2.2 */ +int +nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res) +{ + int r,_status; + nr_stun_message_attribute *mi; + nr_stun_message_attribute *n; + nr_stun_server_client *clnt = 0; + + switch (req->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, 0)) { + nr_stun_form_error_response(req, res, 400, "Missing USERNAME"); + nr_stun_add_realm_and_nonce(0, 0, res); + ABORT(R_ALREADY); + } + + if ((r=nr_stun_get_message_client(ctx, req, &clnt))) { + nr_stun_form_error_response(req, res, 401, "Unrecognized USERNAME"); + nr_stun_add_realm_and_nonce(0, 0, res); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, &mi)) { + nr_stun_form_error_response(req, res, 401, "Missing MESSAGE-INTEGRITY"); + nr_stun_add_realm_and_nonce(0, clnt, res); + ABORT(R_ALREADY); + } + + assert(!mi->u.message_integrity.unknown_user); + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_REALM, 0)) { + nr_stun_form_error_response(req, res, 400, "Missing REALM"); + ABORT(R_ALREADY); + } + + if (!nr_stun_message_has_attribute(req, NR_STUN_ATTR_NONCE, &n)) { + nr_stun_form_error_response(req, res, 400, "Missing NONCE"); + ABORT(R_ALREADY); + } + + assert(sizeof(clnt->nonce) == sizeof(n->u.nonce)); + if (strncmp(clnt->nonce, n->u.nonce, sizeof(n->u.nonce))) { + nr_stun_form_error_response(req, res, 438, "Stale NONCE"); + nr_stun_add_realm_and_nonce(1, clnt, res); + ABORT(R_ALREADY); + } + + if (!mi->u.message_integrity.valid) { + nr_stun_form_error_response(req, res, 401, "Bad MESSAGE-INTEGRITY"); + nr_stun_add_realm_and_nonce(0, clnt, res); + ABORT(R_ALREADY); + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to do in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + + return _status; +} + +/* draft-ietf-behave-rfc3489bis-10.txt S 10.2.3 */ +int +nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx) +{ + int _status; + nr_stun_message_attribute *attr; + + switch (res->header.magic_cookie) { + default: + /* in RFC 3489 there is no magic cookie, it's part of the transaction ID */ + /* drop thru */ + case NR_STUN_MAGIC_COOKIE: + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_REALM, &attr)) { + RFREE(ctx->realm); + ctx->realm = r_strdup(attr->u.realm); + if (!ctx->realm) + ABORT(R_NO_MEMORY); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing REALM"); + ABORT(R_REJECTED); + } + + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_NONCE, &attr)) { + RFREE(ctx->nonce); + ctx->nonce = r_strdup(attr->u.nonce); + if (!ctx->nonce) + ABORT(R_NO_MEMORY); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing NONCE"); + ABORT(R_REJECTED); + } + + if (nr_stun_message_has_attribute(res, NR_STUN_ATTR_MESSAGE_INTEGRITY, &attr)) { + if (!attr->u.message_integrity.valid) { + r_log(NR_LOG_STUN, LOG_WARNING, "Bad MESSAGE-INTEGRITY"); + ABORT(R_REJECTED); + } + } + + break; + +#ifdef USE_STUND_0_96 + case NR_STUN_MAGIC_COOKIE2: + /* nothing to check in this case */ + break; +#endif /* USE_STUND_0_96 */ + } + + _status=0; + abort: + return _status; +} + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h new file mode 100644 index 0000000000..5975670779 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_proc.h @@ -0,0 +1,53 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_proc_h +#define _stun_proc_h + +#include "stun.h" + +int nr_stun_receive_message(nr_stun_message *req, nr_stun_message *msg); +int nr_stun_process_request(nr_stun_message *req, nr_stun_message *res); +int nr_stun_process_indication(nr_stun_message *ind); +int nr_stun_process_success_response(nr_stun_message *res); +int nr_stun_process_error_response(nr_stun_message *res, UINT2 *error_code); + +int nr_stun_receive_request_or_indication_short_term_auth(nr_stun_message *msg, nr_stun_message *res); +int nr_stun_receive_response_short_term_auth(nr_stun_message *res); + +int nr_stun_receive_request_long_term_auth(nr_stun_message *req, nr_stun_server_ctx *ctx, nr_stun_message *res); +int nr_stun_receive_response_long_term_auth(nr_stun_message *res, nr_stun_client_ctx *ctx); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h new file mode 100644 index 0000000000..2167fcd91b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_reg.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_reg_h +#define _stun_reg_h +#ifdef __cplusplus +using namespace std; +extern "C" { +#endif /* __cplusplus */ + +#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT "stun.client.retransmission_timeout" +#define NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF "stun.client.retransmission_backoff_factor" +#define NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS "stun.client.maximum_transmits" +#define NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF "stun.client.final_retransmit_backoff" + +#define NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS "stun.allow_loopback" +#define NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS "stun.allow_link_local" +#define NR_STUN_REG_PREF_ADDRESS_PRFX "stun.address" +#define NR_STUN_REG_PREF_SERVER_NAME "stun.server.name" +#define NR_STUN_REG_PREF_SERVER_NONCE_SIZE "stun.server.nonce_size" +#define NR_STUN_REG_PREF_SERVER_REALM "stun.server.realm" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c new file mode 100644 index 0000000000..b92b6b5ab6 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.c @@ -0,0 +1,468 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "nr_api.h" +#include "stun.h" + +static int nr_stun_server_destroy_client(nr_stun_server_client *clnt); +static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt); +static int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res); + + +int nr_stun_server_ctx_create(char *label, nr_stun_server_ctx **ctxp) + { + int r,_status; + nr_stun_server_ctx *ctx=0; + + if ((r=nr_stun_startup())) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_stun_server_ctx)))) + ABORT(R_NO_MEMORY); + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + STAILQ_INIT(&ctx->clients); + + *ctxp=ctx; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp) + { + nr_stun_server_ctx *ctx; + nr_stun_server_client *clnt1,*clnt2; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + + STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) { + nr_stun_server_destroy_client(clnt1); + } + + nr_stun_server_destroy_client(ctx->default_client); + + RFREE(ctx->label); + RFREE(ctx); + + return(0); + } + +static int nr_stun_server_client_create(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg, nr_stun_server_client **clntp) + { + nr_stun_server_client *clnt=0; + int r,_status; + + if(!(clnt=RCALLOC(sizeof(nr_stun_server_client)))) + ABORT(R_NO_MEMORY); + + if(!(clnt->label=r_strdup(client_label))) + ABORT(R_NO_MEMORY); + + if(!(clnt->username=r_strdup(user))) + ABORT(R_NO_MEMORY); + + if(r=r_data_copy(&clnt->password,pass)) + ABORT(r); + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user); + clnt->stun_server_cb=cb; + clnt->cb_arg=cb_arg; + + *clntp = clnt; + _status=0; + abort: + if(_status){ + nr_stun_server_destroy_client(clnt); + } + return(_status); + } + +int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg) + { + int r,_status; + nr_stun_server_client *clnt; + + if (r=nr_stun_server_client_create(ctx, client_label, user, pass, cb, cb_arg, &clnt)) + ABORT(r); + + STAILQ_INSERT_TAIL(&ctx->clients,clnt,entry); + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg) + { + int r,_status; + nr_stun_server_client *clnt; + + assert(!ctx->default_client); + if (ctx->default_client) + ABORT(R_INTERNAL); + + if (r=nr_stun_server_client_create(ctx, "default_client", ufrag, pass, cb, cb_arg, &clnt)) + ABORT(r); + + ctx->default_client = clnt; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg) + { + nr_stun_server_client *clnt1,*clnt2; + int found = 0; + + STAILQ_FOREACH_SAFE(clnt1, &ctx->clients, entry, clnt2) { + if(clnt1->cb_arg == cb_arg) { + STAILQ_REMOVE(&ctx->clients, clnt1, nr_stun_server_client_, entry); + nr_stun_server_destroy_client(clnt1); + found++; + } + } + + if (!found) + ERETURN(R_NOT_FOUND); + + return 0; + } + +static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **password) + { + int _status; + nr_stun_server_ctx *ctx = (nr_stun_server_ctx*)arg; + nr_stun_server_client *clnt = 0; + nr_stun_message_attribute *username_attribute; + + if ((nr_stun_get_message_client(ctx, msg, &clnt))) { + if (! nr_stun_message_has_attribute(msg, NR_STUN_ATTR_USERNAME, &username_attribute)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); + } + + /* Although this is an exceptional condition, we'll already have seen a + * NOTICE-level log message about the unknown user, so additional log + * messages at any level higher than DEBUG are unnecessary. */ + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username); + ABORT(R_NOT_FOUND); + } + + *password = &clnt->password; + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_process_request_auth_checks(nr_stun_server_ctx *ctx, nr_stun_message *req, int auth_rule, nr_stun_message *res) + { + int r,_status; + + if (nr_stun_message_has_attribute(req, NR_STUN_ATTR_MESSAGE_INTEGRITY, 0) + || !(auth_rule & NR_STUN_AUTH_RULE_OPTIONAL)) { + /* favor long term credentials over short term, if both are supported */ + + if (auth_rule & NR_STUN_AUTH_RULE_LONG_TERM) { + if ((r=nr_stun_receive_request_long_term_auth(req, ctx, res))) + ABORT(r); + } + else if (auth_rule & NR_STUN_AUTH_RULE_SHORT_TERM) { + if ((r=nr_stun_receive_request_or_indication_short_term_auth(req, res))) + ABORT(r); + } + } + + _status=0; + abort: + return(_status); + } + +int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule) + { + int r,_status; + char string[256]; + nr_stun_message *req = 0; + nr_stun_message *res = 0; + nr_stun_server_client *clnt = 0; + nr_stun_server_request info; + int error; + int dont_free = 0; + nr_transport_addr my_addr; + + if ((r=nr_socket_getaddr(sock, &my_addr))) { + ABORT(r); + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Received(my_addr=%s,peer_addr=%s)",ctx->label,my_addr.as_string,peer_addr->as_string); + + snprintf(string, sizeof(string)-1, "STUN-SERVER(%s): Received ", ctx->label); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)msg, len); + + memset(&info,0,sizeof(info)); + + if ((r=nr_stun_message_create2(&req, (UCHAR*)msg, len))) + ABORT(r); + + if ((r=nr_stun_message_create(&res))) + ABORT(r); + + if ((r=nr_stun_decode_message(req, nr_stun_server_get_password, ctx))) { + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Failed to decode request"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + if ((r=nr_stun_receive_message(0, req))) { + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Section 7.3 check failed"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_REQUEST + && NR_STUN_GET_TYPE_CLASS(req->header.type) != NR_CLASS_INDICATION) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Illegal message type: %04x",ctx->label,req->header.type); + /* RFC5389 S 7.3 says "If any errors are detected, the message is + * silently discarded." */ +#ifndef USE_STUN_PEDANTIC + /* ... but that seems like a bad idea, at least return a 400 so + * that the server isn't a black hole to the client */ + nr_stun_form_error_response(req, res, 400, "Bad Request - Unsupported message type"); + ABORT(R_ALREADY); +#endif /* USE_STUN_PEDANTIC */ + ABORT(R_REJECTED); + } + + /* "The STUN agent then does any checks that are required by a + * authentication mechanism that the usage has specified" */ + if ((r=nr_stun_server_process_request_auth_checks(ctx, req, auth_rule, res))) + ABORT(r); + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) { + if ((r=nr_stun_process_indication(req))) + ABORT(r); + } + else { + if ((r=nr_stun_process_request(req, res))) + ABORT(r); + } + + assert(res->header.type == 0); + + clnt = 0; + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_REQUEST) { + if ((nr_stun_get_message_client(ctx, req, &clnt))) { + if ((r=nr_stun_form_success_response(req, peer_addr, 0, res))) + ABORT(r); + } + else { + if ((r=nr_stun_form_success_response(req, peer_addr, &clnt->password, res))) + ABORT(r); + } + } + + if(clnt && clnt->stun_server_cb){ + r_log(NR_LOG_STUN,LOG_DEBUG,"Entering STUN server callback"); + + /* Set up the info */ + if(r=nr_transport_addr_copy(&info.src_addr,peer_addr)) + ABORT(r); + + info.request = req; + info.response = res; + + error = 0; + dont_free = 0; + if (clnt->stun_server_cb(clnt->cb_arg,ctx,sock,&info,&dont_free,&error)) { + if (error == 0) + error = 500; + + nr_stun_form_error_response(req, res, error, "ICE Failure"); + ABORT(R_ALREADY); + } + } + + _status=0; + abort: + if (!res) + goto skip_response; + + if (NR_STUN_GET_TYPE_CLASS(req->header.type) == NR_CLASS_INDICATION) + goto skip_response; + + /* Now respond */ + + if (_status != 0 && ! nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)) + nr_stun_form_error_response(req, res, 500, "Failed to specify error"); + + if ((r=nr_stun_server_send_response(ctx, sock, peer_addr, res, clnt))) { + r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Failed sending response (my_addr=%s,peer_addr=%s)",ctx->label,my_addr.as_string,peer_addr->as_string); + _status = R_FAILED; + } + +#if 0 + /* EKR: suppressed these checks because if you have an error when + you are sending an error, things go wonky */ +#ifdef SANITY_CHECKS + if (_status == R_ALREADY) { + assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_ERROR_RESPONSE); + assert(nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)); + } + else { + assert(NR_STUN_GET_TYPE_CLASS(res->header.type) == NR_CLASS_RESPONSE); + assert(!nr_stun_message_has_attribute(res, NR_STUN_ATTR_ERROR_CODE, 0)); + } +#endif /* SANITY_CHECKS */ +#endif + + if (0) { + skip_response: + _status = 0; + } + + if (!dont_free) { + nr_stun_message_destroy(&res); + nr_stun_message_destroy(&req); + } + + return(_status); + } + +static int nr_stun_server_send_response(nr_stun_server_ctx *ctx, nr_socket *sock, nr_transport_addr *peer_addr, nr_stun_message *res, nr_stun_server_client *clnt) + { + int r,_status; + char string[256]; + nr_transport_addr my_addr; + + if ((r=nr_socket_getaddr(sock, &my_addr))) { + ABORT(r); + } + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(label=%s): Sending(my_addr=%s,peer_addr=%s)",ctx->label,my_addr.as_string,peer_addr->as_string); + + if ((r=nr_stun_encode_message(res))) { + /* should never happen */ + r_log(NR_LOG_STUN,LOG_ERR,"STUN-SERVER(label=%s): Unable to encode message", ctx->label); + } + else { + snprintf(string, sizeof(string)-1, "STUN(%s): Sending to %s ", ctx->label, peer_addr->as_string); + r_dump(NR_LOG_STUN, LOG_DEBUG, string, (char*)res->buffer, res->length); + + if(r=nr_socket_sendto(sock,res->buffer,res->length,0,peer_addr)) + ABORT(r); + } + + _status=0; + abort: + return(_status); + } + +static int nr_stun_server_destroy_client(nr_stun_server_client *clnt) + { + if (!clnt) + return 0; + + RFREE(clnt->label); + RFREE(clnt->username); + r_data_zfree(&clnt->password); + + RFREE(clnt); + return(0); + } + +int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **out) + { + int _status; + nr_stun_message_attribute *attr; + nr_stun_server_client *clnt=0; + + if (! nr_stun_message_has_attribute(req, NR_STUN_ATTR_USERNAME, &attr)) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Missing Username",ctx->label); + ABORT(R_NOT_FOUND); + } + + STAILQ_FOREACH(clnt, &ctx->clients, entry) { + if (!strncmp(clnt->username, attr->u.username, + sizeof(attr->u.username))) + break; + } + + if (!clnt && ctx->default_client) { + /* If we can't find a specific client see if this matches the default, + which means that the username starts with our ufrag. + */ + char *colon = strchr(attr->u.username, ':'); + if (colon && !strncmp(ctx->default_client->username, + attr->u.username, + colon - attr->u.username)) { + clnt = ctx->default_client; + r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Falling back to default client, username=: %s",ctx->label,attr->u.username); + } + } + + if (!clnt) { + r_log(NR_LOG_STUN,LOG_WARNING,"STUN-SERVER(%s): Request from unknown user: %s",ctx->label,attr->u.username); + ABORT(R_NOT_FOUND); + } + + *out = clnt; + + _status=0; + abort: + return(_status); + } + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h new file mode 100644 index 0000000000..328675ee22 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_server_ctx.h @@ -0,0 +1,80 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_server_ctx_h +#define _stun_server_ctx_h + +typedef struct nr_stun_server_ctx_ nr_stun_server_ctx; +typedef struct nr_stun_server_client_ nr_stun_server_client; + +#include "stun_util.h" + + +typedef struct nr_stun_server_request_{ + nr_transport_addr src_addr; + nr_stun_message *request; + nr_stun_message *response; +} nr_stun_server_request; + +typedef int (*nr_stun_server_cb)(void *cb_arg, struct nr_stun_server_ctx_ *ctx,nr_socket *sock,nr_stun_server_request *req, int *dont_free, int *error); + +struct nr_stun_server_client_ { + char *label; + char *username; + Data password; + nr_stun_server_cb stun_server_cb; + void *cb_arg; + char nonce[NR_STUN_MAX_NONCE_BYTES+1]; /* +1 for \0 */ + STAILQ_ENTRY(nr_stun_server_client_) entry; +}; + +typedef STAILQ_HEAD(nr_stun_server_client_head_, nr_stun_server_client_) nr_stun_server_client_head; + +struct nr_stun_server_ctx_ { + char *label; + nr_stun_server_client_head clients; + nr_stun_server_client *default_client; +}; + + +int nr_stun_server_ctx_create(char *label, nr_stun_server_ctx **ctxp); +int nr_stun_server_ctx_destroy(nr_stun_server_ctx **ctxp); +int nr_stun_server_add_client(nr_stun_server_ctx *ctx, char *client_label, char *user, Data *pass, nr_stun_server_cb cb, void *cb_arg); +int nr_stun_server_remove_client(nr_stun_server_ctx *ctx, void *cb_arg); +int nr_stun_server_add_default_client(nr_stun_server_ctx *ctx, char *ufrag, Data *pass, nr_stun_server_cb cb, void *cb_arg); +int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, char *msg, int len, nr_transport_addr *peer_addr, int auth_rule); +int nr_stun_get_message_client(nr_stun_server_ctx *ctx, nr_stun_message *req, nr_stun_server_client **clnt); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c new file mode 100644 index 0000000000..cbb0128924 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.c @@ -0,0 +1,352 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#ifdef WIN32 +#include +#include +#include +#include +#else /* UNIX */ +#include +#endif /* end UNIX */ +#include + +#include "stun.h" +#include "stun_reg.h" +#include "registry.h" +#include "addrs.h" +#include "transport_addr_reg.h" +#include "nr_crypto.h" +#include "hex.h" + + +int NR_LOG_STUN = 0; + +int +nr_stun_startup(void) +{ + int r,_status; + + if ((r=r_log_register("stun", &NR_LOG_STUN))) + ABORT(r); + + _status=0; + abort: + return _status; +} + +int +nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to) +{ + int _status; + + switch (from->ip_version) { + case NR_IPV4: + nr_ip4_port_to_transport_addr( + (ntohl(from->u.addr4.sin_addr.s_addr) ^ magicCookie), + (ntohs(from->u.addr4.sin_port) ^ (magicCookie>>16)), + from->protocol, to); + break; + case NR_IPV6: + { + union { + unsigned char addr[16]; + UINT4 addr32[4]; + } maskedAddr; + + maskedAddr.addr32[0] = htonl(magicCookie); /* Passed in host byte order */ + memcpy(&maskedAddr.addr32[1], transactionId.octet, sizeof(transactionId)); + + /* We now have the mask in network byte order */ + /* Xor the address in network byte order */ + for (size_t i = 0; i < sizeof(maskedAddr); ++i) { + maskedAddr.addr[i] ^= from->u.addr6.sin6_addr.s6_addr[i]; + } + + nr_ip6_port_to_transport_addr( + (struct in6_addr*)&maskedAddr, + (ntohs(from->u.addr6.sin6_port) ^ (magicCookie>>16)), + from->protocol, to); + } + break; + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + _status = 0; + abort: + return _status; +} + +int +nr_stun_filter_local_addresses(nr_local_addr addrs[], int *count) +{ + int r,_status; + char allow_loopback = 0; + char allow_link_local = 0; + + if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, + &allow_loopback))) { + if (r != R_NOT_FOUND) { + ABORT(r); + } + } + + if ((r=NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, + &allow_link_local))) { + if (r != R_NOT_FOUND) { + ABORT(r); + } + } + + if ((r=nr_stun_filter_addrs(addrs, + !allow_loopback, + !allow_link_local, + count))) { + ABORT(r); + } + + _status=0; + abort: + return _status; +} + +int +nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count) +{ + int r,_status; + //NR_registry *children = 0; + + *count = 0; + +#if 0 + // this really goes with the code commented out below. (mjf) + if ((r=NR_reg_get_child_count(NR_STUN_REG_PREF_ADDRESS_PRFX, (unsigned int*)count))) + if (r != R_NOT_FOUND) + ABORT(r); +#endif + + if (*count == 0) { + if ((r=nr_stun_get_addrs(addrs, maxaddrs, count))) + ABORT(r); + + goto done; + } + + if (*count >= maxaddrs) { + r_log(NR_LOG_STUN, LOG_INFO, "Address list truncated from %d to %d", *count, maxaddrs); + *count = maxaddrs; + } + +#if 0 + if (*count > 0) { + /* TODO(ekr@rtfm.com): Commented out 2012-07-26. + + This code is currently not used in Firefox and needs to be + ported to 64-bit */ + children = RCALLOC((*count + 10) * sizeof(*children)); + if (!children) + ABORT(R_NO_MEMORY); + + assert(sizeof(size_t) == sizeof(*count)); + + if ((r=NR_reg_get_children(NR_STUN_REG_PREF_ADDRESS_PRFX, children, (size_t)(*count + 10), (size_t*)count))) + ABORT(r); + + for (i = 0; i < *count; ++i) { + if ((r=nr_reg_get_transport_addr(children[i], 0, &addrs[i].addr))) + ABORT(r); + } + } +#endif + + done: + + _status=0; + abort: + //RFREE(children); + return _status; +} + +int +nr_stun_different_transaction(UCHAR *msg, size_t len, nr_stun_message *req) +{ + int _status; + nr_stun_message_header header; + char reqid[44]; + char msgid[44]; + size_t unused; + + if (sizeof(header) > len) + ABORT(R_FAILED); + + assert(sizeof(header.id) == sizeof(UINT12)); + + memcpy(&header, msg, sizeof(header)); + + if (memcmp(&req->header.id, &header.id, sizeof(header.id))) { + nr_nbin2hex((UCHAR*)&req->header.id, sizeof(req->header.id), reqid, sizeof(reqid), &unused); + nr_nbin2hex((UCHAR*)&header.id, sizeof(header.id), msgid, sizeof(msgid), &unused); + r_log(NR_LOG_STUN, LOG_DEBUG, "Mismatched message IDs %s/%s", reqid, msgid); + ABORT(R_NOT_FOUND); + } + + _status=0; + abort: + return _status; +} + +char* +nr_stun_msg_type(int type) +{ + char *ret = 0; + + switch (type) { + case NR_STUN_MSG_BINDING_REQUEST: + ret = "BINDING-REQUEST"; + break; + case NR_STUN_MSG_BINDING_INDICATION: + ret = "BINDING-INDICATION"; + break; + case NR_STUN_MSG_BINDING_RESPONSE: + ret = "BINDING-RESPONSE"; + break; + case NR_STUN_MSG_BINDING_ERROR_RESPONSE: + ret = "BINDING-ERROR-RESPONSE"; + break; + +#ifdef USE_TURN + case NR_STUN_MSG_ALLOCATE_REQUEST: + ret = "ALLOCATE-REQUEST"; + break; + case NR_STUN_MSG_ALLOCATE_RESPONSE: + ret = "ALLOCATE-RESPONSE"; + break; + case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE: + ret = "ALLOCATE-ERROR-RESPONSE"; + break; + case NR_STUN_MSG_REFRESH_REQUEST: + ret = "REFRESH-REQUEST"; + break; + case NR_STUN_MSG_REFRESH_RESPONSE: + ret = "REFRESH-RESPONSE"; + break; + case NR_STUN_MSG_REFRESH_ERROR_RESPONSE: + ret = "REFRESH-ERROR-RESPONSE"; + break; + case NR_STUN_MSG_SEND_INDICATION: + ret = "SEND-INDICATION"; + break; + case NR_STUN_MSG_DATA_INDICATION: + ret = "DATA-INDICATION"; + break; + case NR_STUN_MSG_PERMISSION_REQUEST: + ret = "PERMISSION-REQUEST"; + break; + case NR_STUN_MSG_PERMISSION_RESPONSE: + ret = "PERMISSION-RESPONSE"; + break; + case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE: + ret = "PERMISSION-ERROR-RESPONSE"; + break; +#endif /* USE_TURN */ + + default: + /* ret remains 0 */ + break; + } + + return ret; +} + +int +nr_random_alphanum(char *alphanum, int size) +{ + static char alphanums[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' }; + int i; + + nr_crypto_random_bytes((UCHAR*)alphanum, size); + + /* now convert from binary to alphanumeric */ + for (i = 0; i < size; ++i) + alphanum[i] = alphanums[(UCHAR)alphanum[i]]; + + return 0; +} + +#ifndef UINT2_MAX +#define UINT2_MAX ((UINT2)(65535U)) +#endif + +void nr_accumulate_count(UINT2* orig_count, UINT2 add_count) + { + if (UINT2_MAX - add_count < *orig_count) { + // don't rollover, just stop accumulating at MAX value + *orig_count = UINT2_MAX; + } else { + *orig_count += add_count; + } + } diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h new file mode 100644 index 0000000000..379b4fdd3d --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_util.h @@ -0,0 +1,62 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _stun_util_h +#define _stun_util_h + +#include "stun.h" +#include "local_addr.h" + +extern int NR_LOG_STUN; + +int nr_stun_startup(void); + +int nr_stun_xor_mapped_address(UINT4 magicCookie, UINT12 transactionId, nr_transport_addr *from, nr_transport_addr *to); + +// removes duplicates, and based on prefs, loopback and link_local addresses +int nr_stun_filter_local_addresses(nr_local_addr addrs[], int *count); + +int nr_stun_find_local_addresses(nr_local_addr addrs[], int maxaddrs, int *count); + +int nr_stun_different_transaction(UCHAR *msg, size_t len, nr_stun_message *req); + +char* nr_stun_msg_type(int type); + +int nr_random_alphanum(char *alphanum, int size); + +// accumulate a count without worrying about rollover +void nr_accumulate_count(UINT2* orig_count, UINT2 add_count); + +#endif + diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c new file mode 100644 index 0000000000..707d43a4a9 --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.c @@ -0,0 +1,1277 @@ +/* + Copyright (c) 2007, Adobe Systems, Incorporated + Copyright (c) 2013, Mozilla + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef USE_TURN + +#include +#include + +#include "nr_api.h" +#include "r_time.h" +#include "async_timer.h" +#include "nr_socket_buffered_stun.h" +#include "stun.h" +#include "turn_client_ctx.h" +#include "ice_ctx.h" + +int NR_LOG_TURN = 0; + +#define TURN_MAX_PENDING_BYTES 32000 + +#define TURN_RTO 100 /* Hardcoded RTO estimate */ +#define TURN_LIFETIME_REQUEST_SECONDS 3600 /* One hour */ +#define TURN_USECS_PER_S 1000000 +#define TURN_REFRESH_SLACK_SECONDS 10 /* How long before expiry to refresh + allocations/permissions. The RFC 5766 + Section 7 recommendation if 60 seconds, + but this is silly since the transaction + times out after about 5. */ +#define TURN_PERMISSION_LIFETIME_SECONDS 300 /* 5 minutes. From RFC 5766 2.3 */ + +// Set to enable a temporary fix that will run the TURN reservation keep-alive +// logic when data is received via a TURN relayed path: a DATA_INDICATION packet is received. +// TODO(pkerr@mozilla.com) This should be replace/removed when bug 935806 is implemented. +#define REFRESH_RESERVATION_ON_RECV 1 + +static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int type, + NR_async_cb success_cb, + NR_async_cb failure_cb, + nr_turn_stun_ctx **ctxp); +static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp); +static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg); +static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx, + char *realm, char *nonce); +static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg); +static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx **sctx); +static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx *sctx, + UINT4 lifetime); +static int nr_turn_permission_create(nr_turn_client_ctx *ctx, + const nr_transport_addr *addr, + nr_turn_permission **permp); +static int nr_turn_permission_find(nr_turn_client_ctx *ctx, + const nr_transport_addr *addr, + nr_turn_permission **permp); +static int nr_turn_permission_destroy(nr_turn_permission **permp); +static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg); +static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *cb); +static int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags); + +int nr_transport_addr_listnode_create(const nr_transport_addr *addr, nr_transport_addr_listnode **listnodep) +{ + nr_transport_addr_listnode *listnode = 0; + int r,_status; + + if (!(listnode=RCALLOC(sizeof(nr_transport_addr_listnode)))) { + ABORT(R_NO_MEMORY); + } + + if ((r = nr_transport_addr_copy(&listnode->value, addr))) { + ABORT(r); + } + + *listnodep = listnode; + listnode = 0; + _status = 0; + +abort: + nr_transport_addr_listnode_destroy(&listnode); + return(_status); +} + +void nr_transport_addr_listnode_destroy(nr_transport_addr_listnode **listnode) +{ + RFREE(*listnode); + *listnode = 0; +} + +/* nr_turn_stun_ctx functions */ +static int nr_turn_stun_ctx_create(nr_turn_client_ctx *tctx, int mode, + NR_async_cb success_cb, + NR_async_cb error_cb, + nr_turn_stun_ctx **ctxp) +{ + nr_turn_stun_ctx *sctx = 0; + int r,_status; + char label[256]; + + if (!(sctx=RCALLOC(sizeof(nr_turn_stun_ctx)))) + ABORT(R_NO_MEMORY); + + /* TODO(ekr@rtfm.com): label by phase */ + snprintf(label, sizeof(label), "%s:%s", tctx->label, ":TURN"); + + if ((r=nr_stun_client_ctx_create(label, tctx->sock, &tctx->turn_server_addr, + TURN_RTO, &sctx->stun))) { + ABORT(r); + } + + /* Set the STUN auth parameters, but don't set authentication on. + For that we need the nonce, set in nr_turn_stun_set_auth_params. + */ + sctx->stun->auth_params.username=tctx->username; + INIT_DATA(sctx->stun->auth_params.password, + tctx->password->data, tctx->password->len); + + sctx->tctx=tctx; + sctx->success_cb=success_cb; + sctx->error_cb=error_cb; + sctx->mode=mode; + sctx->last_error_code=0; + STAILQ_INIT(&sctx->addresses_tried); + + /* Add ourselves to the tctx's list */ + STAILQ_INSERT_TAIL(&tctx->stun_ctxs, sctx, entry); + *ctxp=sctx; + + _status=0; +abort: + if (_status) { + nr_turn_stun_ctx_destroy(&sctx); + } + return(_status); +} + +/* Note: this function does not pull us off the tctx's list. */ +static int nr_turn_stun_ctx_destroy(nr_turn_stun_ctx **ctxp) +{ + nr_turn_stun_ctx *ctx; + + if (!ctxp || !*ctxp) + return 0; + + ctx = *ctxp; + *ctxp = 0; + + nr_stun_client_ctx_destroy(&ctx->stun); + RFREE(ctx->realm); + RFREE(ctx->nonce); + + while (!STAILQ_EMPTY(&ctx->addresses_tried)) { + nr_transport_addr_listnode *listnode = STAILQ_FIRST(&ctx->addresses_tried); + STAILQ_REMOVE_HEAD(&ctx->addresses_tried, entry); + nr_transport_addr_listnode_destroy(&listnode); + } + + RFREE(ctx); + + return 0; +} + +static int nr_turn_stun_set_auth_params(nr_turn_stun_ctx *ctx, + char *realm, char *nonce) +{ + int _status; + + RFREE(ctx->realm); + RFREE(ctx->nonce); + + assert(realm); + if (!realm) + ABORT(R_BAD_ARGS); + ctx->realm=r_strdup(realm); + if (!ctx->realm) + ABORT(R_NO_MEMORY); + + assert(nonce); + if (!nonce) + ABORT(R_BAD_ARGS); + ctx->nonce=r_strdup(nonce); + if (!ctx->nonce) + ABORT(R_NO_MEMORY); + + RFREE(ctx->stun->realm); + ctx->stun->realm = r_strdup(ctx->realm); + if (!ctx->stun->realm) + ABORT(R_NO_MEMORY); + + ctx->stun->auth_params.realm = ctx->realm; + ctx->stun->auth_params.nonce = ctx->nonce; + ctx->stun->auth_params.authenticate = 1; /* May already be 1 */ + + _status=0; +abort: + return(_status); +} + +static int nr_turn_stun_ctx_start(nr_turn_stun_ctx *ctx) +{ + int r, _status; + nr_turn_client_ctx *tctx = ctx->tctx; + nr_transport_addr_listnode *address_tried = 0; + + if ((r=nr_stun_client_reset(ctx->stun))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't reset STUN", + tctx->label); + ABORT(r); + } + + if ((r=nr_stun_client_start(ctx->stun, ctx->mode, nr_turn_stun_ctx_cb, ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", + tctx->label); + ABORT(r); + } + + if ((r=nr_transport_addr_listnode_create(&ctx->stun->peer_addr, &address_tried))) { + ABORT(r); + } + + STAILQ_INSERT_TAIL(&ctx->addresses_tried, address_tried, entry); + + _status=0; +abort: + return _status; +} + +static int nr_turn_stun_ctx_handle_redirect(nr_turn_stun_ctx *ctx) +{ + int r, _status; + nr_turn_client_ctx *tctx = ctx->tctx; + nr_stun_message_attribute *ec; + nr_stun_message_attribute *attr; + nr_transport_addr *alternate_addr = 0; + int index = 0; + + if (!tctx->ctx) { + /* If we were to require TCP nr_sockets to allow multiple connect calls by + * disconnecting and re-connecting, we could avoid this requirement. */ + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect is not supported when " + "there is no ICE ctx, and by extension no socket factory, since we " + "need that to create new sockets in the TCP case", + tctx->label); + ABORT(R_BAD_ARGS); + } + + if (ctx->stun->response->header.type != NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect called for something " + "other than an Allocate error response (type=%d)", + tctx->label, ctx->stun->response->header.type); + ABORT(R_BAD_ARGS); + } + + if (!nr_stun_message_has_attribute(ctx->stun->response, + NR_STUN_ATTR_ERROR_CODE, &ec) || + (ec->u.error_code.number != 300)) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect called without a " + "300 response", + tctx->label); + ABORT(R_BAD_ARGS); + } + + while (!alternate_addr && !nr_stun_message_get_attribute( + ctx->stun->response, NR_STUN_ATTR_ALTERNATE_SERVER, index++, &attr)) { + alternate_addr = &attr->u.alternate_server; + + // TODO: Someday we may need to handle IP version switching, but it is + // unclear how that is supposed to work with ICE when the IP version of + // the candidate's base address is fixed... + if (alternate_addr->ip_version != tctx->turn_server_addr.ip_version) { + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): nr_turn_stun_ctx_handle_redirect not trying %s, since it is a different IP version", + tctx->label, alternate_addr->as_string); + alternate_addr = 0; + continue; + } + + /* Check if we've already tried this, and ignore if we have */ + nr_transport_addr_listnode *address_tried = 0; + STAILQ_FOREACH(address_tried, &ctx->addresses_tried, entry) { + /* Ignore protocol */ + alternate_addr->protocol = address_tried->value.protocol; + if (!nr_transport_addr_cmp(alternate_addr, &address_tried->value, NR_TRANSPORT_ADDR_CMP_MODE_ALL)) { + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): nr_turn_stun_ctx_handle_redirect already tried %s, ignoring", + tctx->label, alternate_addr->as_string); + alternate_addr = 0; + break; + } + } + } + + if (!alternate_addr) { + /* Should we use a different error code depending on why we didn't find + * one? (eg; no ALTERNATE-SERVERS at all, none that we have not tried + * already, none that are of a compatible IP version?) */ + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect did not find a viable " + "ALTERNATE-SERVER", + tctx->label); + ABORT(R_FAILED); + } + + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): nr_turn_stun_ctx_handle_redirect trying %s", + tctx->label, alternate_addr->as_string); + + /* This also handles the call to nr_transport_addr_fmt_addr_string */ + if ((r = nr_transport_addr_copy_addrport(&tctx->turn_server_addr, + alternate_addr))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect copying ALTERNATE-SERVER " + "failed(%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + /* TURN server address is now updated. Restart the STUN Allocate ctx. Note + * that we do not attempt to update the local address; if the TURN server + * redirects to something that is not best reached from the already-selected + * local address, oh well. */ + + if (tctx->turn_server_addr.protocol == IPPROTO_TCP) { + /* For TCP, we need to replace the underlying nr_socket, since we cannot + * un-connect it from the old server. */ + /* If we were to require TCP nr_sockets to allow multiple connect calls by + * disconnecting and re-connecting, we could avoid this stuff, and just + * call nr_socket_connect. */ + nr_transport_addr old_local_addr; + nr_socket* new_socket; + if ((r = nr_socket_getaddr(tctx->sock, &old_local_addr))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect " + "failed to get old local address (%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + if ((r = nr_socket_factory_create_socket(tctx->ctx->socket_factory, + &old_local_addr, &new_socket))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect " + "failed to create new raw TCP socket for redirect (%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + if ((r = nr_socket_buffered_stun_reset(tctx->sock, new_socket))) { + /* nr_socket_buffered_stun_reset always takes ownership of |new_socket| */ + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect " + "failed to update raw TCP socket (%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + if ((r = nr_socket_connect(tctx->sock, &tctx->turn_server_addr))) { + if (r != R_WOULDBLOCK) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect nr_socket_connect " + "failed(%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + } + } + + nr_transport_addr_copy(&ctx->stun->peer_addr, &tctx->turn_server_addr); + + if ((r = nr_turn_stun_ctx_start(ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, + "TURN(%s): nr_turn_stun_ctx_handle_redirect nr_turn_stun_ctx_start " + "failed(%d)!", + tctx->label, r); + assert(0); + ABORT(r); + } + + _status = 0; +abort: + return _status; +} + +static void nr_turn_stun_ctx_cb(NR_SOCKET s, int how, void *arg) +{ + int r, _status; + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + ctx->last_error_code = ctx->stun->error_code; + + switch (ctx->stun->state) { + case NR_STUN_CLIENT_STATE_DONE: + /* Save the realm and nonce */ + if (ctx->stun->realm && (!ctx->tctx->realm || strcmp(ctx->stun->realm, + ctx->tctx->realm))) { + RFREE(ctx->tctx->realm); + ctx->tctx->realm = r_strdup(ctx->stun->realm); + if (!ctx->tctx->realm) + ABORT(R_NO_MEMORY); + } + if (ctx->stun->nonce && (!ctx->tctx->nonce || strcmp(ctx->stun->nonce, + ctx->tctx->nonce))) { + RFREE(ctx->tctx->nonce); + ctx->tctx->nonce = r_strdup(ctx->stun->nonce); + if (!ctx->tctx->nonce) + ABORT(R_NO_MEMORY); + } + + ctx->retry_ct=0; + ctx->success_cb(0, 0, ctx); + break; + + case NR_STUN_CLIENT_STATE_FAILED: + /* Special case: if this is an authentication error, + we retry once. This allows the 401/438 nonce retry + paradigm. After that, we fail */ + /* TODO(ekr@rtfm.com): 401 needs a #define */ + /* TODO(ekr@rtfm.com): Add alternate-server (Mozilla bug 857688) */ + if (ctx->stun->error_code == 438) { + // track 438s for ice telemetry + nr_accumulate_count(&(ctx->tctx->cnt_438s), 1); + } + if (ctx->stun->error_code == 401 || ctx->stun->error_code == 438) { + if (ctx->retry_ct > 0) { + if (ctx->stun->error_code == 401) { + // track 401s for ice telemetry + nr_accumulate_count(&(ctx->tctx->cnt_401s), 1); + } + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Exceeded the number of retries", ctx->tctx->label); + ABORT(R_FAILED); + } + + if (!ctx->stun->nonce) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no nonce", ctx->tctx->label); + ABORT(R_FAILED); + } + if (!ctx->stun->realm) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): 401 but no realm", ctx->tctx->label); + ABORT(R_FAILED); + } + + /* Try to retry */ + if ((r=nr_turn_stun_set_auth_params(ctx, ctx->stun->realm, + ctx->stun->nonce))) + ABORT(r); + + ctx->stun->error_code = 0; /* Reset to avoid inf-looping */ + + if ((r=nr_turn_stun_ctx_start(ctx))) { + r_log(NR_LOG_TURN, LOG_ERR, "TURN(%s): Couldn't start STUN", ctx->tctx->label); + ABORT(r); + } + + ctx->retry_ct++; + } else if (ctx->stun->error_code == 300) { + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): Redirect received, restarting TURN", ctx->tctx->label); + /* We don't limit this with a retry counter, we limit redirects by + * checking whether we've tried the ALTERNATE-SERVER yet. */ + ctx->retry_ct = 0; + if ((r = nr_turn_stun_ctx_handle_redirect(ctx))) { + ABORT(r); + } + } else { + ABORT(R_FAILED); + } + break; + + case NR_STUN_CLIENT_STATE_TIMED_OUT: + ABORT(R_FAILED); + break; + + case NR_STUN_CLIENT_STATE_CANCELLED: + assert(0); /* Shouldn't happen */ + return; + break; + + default: + assert(0); /* Shouldn't happen */ + return; + } + + _status=0; +abort: + if (_status) { + ctx->error_cb(0, 0, ctx); + } +} + +/* nr_turn_client_ctx functions */ +int nr_turn_client_ctx_create(const char* label, nr_socket* sock, + const char* username, Data* password, + nr_transport_addr* addr, nr_ice_ctx* ice_ctx, + nr_turn_client_ctx** ctxp) { + nr_turn_client_ctx *ctx=0; + int r,_status; + + if ((r=r_log_register("turn", &NR_LOG_TURN))) + ABORT(r); + + if(!(ctx=RCALLOC(sizeof(nr_turn_client_ctx)))) + ABORT(R_NO_MEMORY); + + STAILQ_INIT(&ctx->stun_ctxs); + STAILQ_INIT(&ctx->permissions); + + if(!(ctx->label=r_strdup(label))) + ABORT(R_NO_MEMORY); + + ctx->sock=sock; + ctx->username = r_strdup(username); + if (!ctx->username) + ABORT(R_NO_MEMORY); + + if ((r=r_data_create(&ctx->password, password->data, password->len))) + ABORT(r); + if ((r=nr_transport_addr_copy(&ctx->turn_server_addr, addr))) + ABORT(r); + + ctx->state = NR_TURN_CLIENT_STATE_INITTED; + if (addr->protocol == IPPROTO_TCP) { + if ((r=nr_socket_connect(ctx->sock, &ctx->turn_server_addr))) { + if (r != R_WOULDBLOCK) + ABORT(r); + } + } + + ctx->ctx = ice_ctx; + + *ctxp=ctx; + + _status=0; +abort: + if(_status){ + nr_turn_client_ctx_destroy(&ctx); + } + return(_status); +} + +int +nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp) +{ + nr_turn_client_ctx *ctx; + + if(!ctxp || !*ctxp) + return(0); + + ctx=*ctxp; + *ctxp = 0; + + if (ctx->label) + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): destroy", ctx->label); + + nr_turn_client_deallocate(ctx); + + /* Cancel frees the rest of our data */ + RFREE(ctx->label); + ctx->label = 0; + + nr_turn_client_cancel(ctx); + + RFREE(ctx->username); + ctx->username = 0; + r_data_destroy(&ctx->password); + RFREE(ctx->nonce); + ctx->nonce = 0; + RFREE(ctx->realm); + ctx->realm = 0; + + /* Destroy the STUN client ctxs */ + while (!STAILQ_EMPTY(&ctx->stun_ctxs)) { + nr_turn_stun_ctx *stun = STAILQ_FIRST(&ctx->stun_ctxs); + STAILQ_REMOVE_HEAD(&ctx->stun_ctxs, entry); + nr_turn_stun_ctx_destroy(&stun); + } + + /* Destroy the permissions */ + while (!STAILQ_EMPTY(&ctx->permissions)) { + nr_turn_permission *perm = STAILQ_FIRST(&ctx->permissions); + STAILQ_REMOVE_HEAD(&ctx->permissions, entry); + nr_turn_permission_destroy(&perm); + } + + RFREE(ctx); + + return(0); +} + +int nr_turn_client_cancel(nr_turn_client_ctx *ctx) +{ + nr_turn_stun_ctx *stun = 0; + + if (ctx->state == NR_TURN_CLIENT_STATE_CANCELLED || + ctx->state == NR_TURN_CLIENT_STATE_FAILED) + return(0); + + if (ctx->label) + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): cancelling", ctx->label); + + /* Cancel the STUN client ctxs */ + stun = STAILQ_FIRST(&ctx->stun_ctxs); + while (stun) { + nr_stun_client_cancel(stun->stun); + stun = STAILQ_NEXT(stun, entry); + } + + /* Cancel the timers, if not already cancelled */ + NR_async_timer_cancel(ctx->connected_timer_handle); + NR_async_timer_cancel(ctx->refresh_timer_handle); + + ctx->state = NR_TURN_CLIENT_STATE_CANCELLED; + + return(0); +} + +int nr_turn_client_send_stun_request(nr_turn_client_ctx *ctx, + nr_stun_message *req, + int flags) +{ + int r,_status; + + if ((r=nr_stun_encode_message(req))) + ABORT(r); + + if ((r=nr_socket_sendto(ctx->sock, + req->buffer, req->length, flags, + &ctx->turn_server_addr))) { + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): Failed sending request", + ctx->label); + ABORT(r); + } + + _status=0; +abort: + return(_status); +} + +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx) +{ + int r,_status; + nr_stun_message *aloc = 0; + nr_stun_client_auth_params auth; + nr_stun_client_refresh_request_params refresh; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + return(0); + + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): deallocating", ctx->label); + + refresh.lifetime_secs = 0; + + auth.username = ctx->username; + INIT_DATA(auth.password, ctx->password->data, ctx->password->len); + + auth.realm = ctx->realm; + auth.nonce = ctx->nonce; + + auth.authenticate = 1; + + if ((r=nr_stun_build_refresh_request(&auth, &refresh, &aloc))) + ABORT(r); + + // We are only sending a single request here because we are in the process of + // shutting everything down. Theoretically we should probably start a seperate + // STUN transaction which outlives the TURN context. + if ((r=nr_turn_client_send_stun_request(ctx, aloc, 0))) + ABORT(r); + + ctx->state = NR_TURN_CLIENT_STATE_DEALLOCATING; + + _status=0; +abort: + nr_stun_message_destroy(&aloc); + return(_status); +} + +static void nr_turn_client_fire_finished_cb(nr_turn_client_ctx *ctx) + { + if (ctx->finished_cb) { + NR_async_cb finished_cb=ctx->finished_cb; + ctx->finished_cb=0; + finished_cb(0, 0, ctx->cb_arg); + } + } + +int nr_turn_client_failed(nr_turn_client_ctx *ctx) +{ + if (ctx->state == NR_TURN_CLIENT_STATE_FAILED || + ctx->state == NR_TURN_CLIENT_STATE_CANCELLED) + return(0); + + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s) failed", ctx->label); + nr_turn_client_cancel(ctx); + ctx->state = NR_TURN_CLIENT_STATE_FAILED; + nr_turn_client_fire_finished_cb(ctx); + + return(0); +} + +int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx, + nr_transport_addr *relayed_address) +{ + int r, _status; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + if (r=nr_transport_addr_copy(relayed_address, &ctx->relay_addr)) + ABORT(r); + + _status=0; +abort: + return(_status); +} + +int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx, + nr_transport_addr *mapped_address) +{ + int r, _status; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + if (r=nr_transport_addr_copy(mapped_address, &ctx->mapped_addr)) + ABORT(r); + + _status=0; +abort: + return(_status); +} + +static void nr_turn_client_allocate_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + nr_turn_stun_ctx *refresh_ctx; + int r,_status; + + ctx->tctx->state = NR_TURN_CLIENT_STATE_ALLOCATED; + + if ((r=nr_transport_addr_copy( + &ctx->tctx->relay_addr, + &ctx->stun->results.allocate_response.relay_addr))) + ABORT(r); + + if ((r=nr_transport_addr_copy( + &ctx->tctx->mapped_addr, + &ctx->stun->results.allocate_response.mapped_addr))) + ABORT(r); + + if ((r=nr_turn_client_refresh_setup(ctx->tctx, &refresh_ctx))) + ABORT(r); + + if ((r=nr_turn_client_start_refresh_timer( + ctx->tctx, refresh_ctx, + ctx->stun->results.allocate_response.lifetime_secs))) + ABORT(r); + + r_log(NR_LOG_TURN, LOG_INFO, + "TURN(%s): Succesfully allocated addr %s lifetime=%u", + ctx->tctx->label, + ctx->tctx->relay_addr.as_string, + ctx->stun->results.allocate_response.lifetime_secs); + + nr_turn_client_fire_finished_cb(ctx->tctx); + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } +} + +static void nr_turn_client_error_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, %s", + ctx->tctx->label, ctx->mode, __FUNCTION__); + + nr_turn_client_failed(ctx->tctx); +} + +static void nr_turn_client_permission_error_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + + if (ctx->last_error_code == 403) { + // track 403s for ice telemetry + nr_accumulate_count(&(ctx->tctx->cnt_403s), 1); + r_log(NR_LOG_TURN, LOG_WARNING, "TURN(%s): mode %d, permission denied", + ctx->tctx->label, ctx->mode); + + } else{ + nr_turn_client_error_cb(0, 0, ctx); + } +} + +int nr_turn_client_allocate(nr_turn_client_ctx *ctx, + NR_async_cb finished_cb, void *cb_arg) +{ + nr_turn_stun_ctx *stun = 0; + int r,_status; + + if(ctx->state == NR_TURN_CLIENT_STATE_FAILED || + ctx->state == NR_TURN_CLIENT_STATE_CANCELLED){ + /* TURN TCP contexts can fail before we ever try to form an allocation, + * since the TCP connection can fail. It is also conceivable that a TURN + * TCP context could be cancelled before we are done forming all + * allocations (although we do not do this at the time this code was + * written) */ + assert(ctx->turn_server_addr.protocol == IPPROTO_TCP); + ABORT(R_NOT_FOUND); + } + + assert(ctx->state == NR_TURN_CLIENT_STATE_INITTED); + + ctx->finished_cb=finished_cb; + ctx->cb_arg=cb_arg; + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_ALLOCATE_REQUEST, + nr_turn_client_allocate_cb, + nr_turn_client_error_cb, + &stun))) + ABORT(r); + stun->stun->params.allocate_request.lifetime_secs = + TURN_LIFETIME_REQUEST_SECONDS; + + if (ctx->state == NR_TURN_CLIENT_STATE_INITTED) { + if ((r=nr_turn_stun_ctx_start(stun))) + ABORT(r); + ctx->state = NR_TURN_CLIENT_STATE_ALLOCATING; + } else { + ABORT(R_ALREADY); + } + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx); + } + + return(_status); +} + +int nr_turn_client_process_response(nr_turn_client_ctx *ctx, + UCHAR *msg, int len, + nr_transport_addr *turn_server_addr) +{ + int r, _status; + nr_turn_stun_ctx *sc1; + + switch (ctx->state) { + case NR_TURN_CLIENT_STATE_ALLOCATING: + case NR_TURN_CLIENT_STATE_ALLOCATED: + break; + default: + ABORT(R_FAILED); + } + + sc1 = STAILQ_FIRST(&ctx->stun_ctxs); + while (sc1) { + r = nr_stun_client_process_response(sc1->stun, msg, len, turn_server_addr); + if (!r) + break; + if (r==R_RETRY) /* Likely a 401 and we will retry */ + break; + if (r != R_REJECTED) + ABORT(r); + sc1 = STAILQ_NEXT(sc1, entry); + } + if (!sc1) + ABORT(R_REJECTED); + + _status=0; +abort: + return(_status); +} + +static int nr_turn_client_refresh_setup(nr_turn_client_ctx *ctx, + nr_turn_stun_ctx **sctx) +{ + nr_turn_stun_ctx *stun = 0; + int r,_status; + + assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED); + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_NOT_PERMITTED); + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_REFRESH_REQUEST, + nr_turn_client_refresh_cb, + nr_turn_client_error_cb, + &stun))) + ABORT(r); + + if ((r=nr_turn_stun_set_auth_params(stun, ctx->realm, ctx->nonce))) + ABORT(r); + + stun->stun->params.refresh_request.lifetime_secs = + TURN_LIFETIME_REQUEST_SECONDS; + + + *sctx=stun; + + _status=0; +abort: + return(_status); +} + +static int nr_turn_client_start_refresh_timer(nr_turn_client_ctx *tctx, + nr_turn_stun_ctx *sctx, + UINT4 lifetime) +{ + int _status; + + assert(!tctx->refresh_timer_handle); + + if (lifetime <= TURN_REFRESH_SLACK_SECONDS) { + r_log(NR_LOG_TURN, LOG_ERR, "Too short lifetime specified for turn %u", lifetime); + ABORT(R_BAD_DATA); + } + + if (lifetime > 3600) + lifetime = 3600; + + lifetime -= TURN_REFRESH_SLACK_SECONDS; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Setting refresh timer for %u seconds", + tctx->label, lifetime); + NR_ASYNC_TIMER_SET(lifetime * 1000, nr_turn_client_refresh_timer_cb, sctx, + &tctx->refresh_timer_handle); + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(tctx); + } + return _status; +} + +static void nr_turn_client_refresh_timer_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + int r,_status; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh timer fired", + ctx->tctx->label); + + ctx->tctx->refresh_timer_handle=0; + if ((r=nr_turn_stun_ctx_start(ctx))) { + ABORT(r); + } + + _status=0; +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } + return; +} + +static void nr_turn_client_refresh_cb(NR_SOCKET s, int how, void *arg) +{ + int r, _status; + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + /* Save lifetime from the reset */ + UINT4 lifetime = ctx->stun->results.refresh_response.lifetime_secs; + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Refresh succeeded. lifetime=%u", + ctx->tctx->label, lifetime); + + if ((r=nr_turn_client_start_refresh_timer( + ctx->tctx, ctx, lifetime))) + ABORT(r); + + _status=0; + +abort: + if (_status) { + nr_turn_client_failed(ctx->tctx); + } +} + +/* TODO(ekr@rtfm.com): We currently don't support channels. + We might in the future. Mozilla bug 857736 */ +int nr_turn_client_send_indication(nr_turn_client_ctx *ctx, + const UCHAR *msg, size_t len, + int flags, const nr_transport_addr *remote_addr) +{ + int r,_status; + nr_stun_client_send_indication_params params = { { 0 } }; + nr_stun_message *ind = 0; + + if (ctx->state != NR_TURN_CLIENT_STATE_ALLOCATED) + ABORT(R_FAILED); + + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Send indication len=%zu", + ctx->label, len); + + if ((r=nr_turn_client_ensure_perm(ctx, remote_addr))) + ABORT(r); + + if ((r=nr_transport_addr_copy(¶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, const nr_transport_addr *addr) +{ + int r, _status; + nr_turn_permission *perm = 0; + UINT8 now; + UINT8 turn_permission_refresh = (TURN_PERMISSION_LIFETIME_SECONDS - + TURN_REFRESH_SLACK_SECONDS) * TURN_USECS_PER_S; + + if ((r=nr_turn_permission_find(ctx, addr, &perm))) { + if (r == R_NOT_FOUND) { + if ((r=nr_turn_permission_create(ctx, addr, &perm))) + ABORT(r); + } + else { + ABORT(r); + } + } + + assert(perm); + + /* Now check that the permission is up-to-date */ + now = r_gettimeint(); + + if ((now - perm->last_used) > turn_permission_refresh) { + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Permission for %s requires refresh", + ctx->label, perm->addr.as_string); + + if ((r=nr_turn_stun_ctx_start(perm->stun))) + ABORT(r); + + perm->last_used = now; /* Update the time now so we don't retry on + next packet */ + } + + _status=0; +abort: + return(_status); +} + +static int nr_turn_permission_create(nr_turn_client_ctx *ctx, const nr_transport_addr *addr, + nr_turn_permission **permp) +{ + int r, _status; + nr_turn_permission *perm = 0; + + assert(ctx->state == NR_TURN_CLIENT_STATE_ALLOCATED); + + r_log(NR_LOG_TURN, LOG_INFO, "TURN(%s): Creating permission for %s", + ctx->label, addr->as_string); + + if (!(perm = RCALLOC(sizeof(nr_turn_permission)))) + ABORT(R_NO_MEMORY); + + if ((r=nr_transport_addr_copy(&perm->addr, addr))) + ABORT(r); + + perm->last_used = 0; + + if ((r=nr_turn_stun_ctx_create(ctx, NR_TURN_CLIENT_MODE_PERMISSION_REQUEST, + nr_turn_client_permissions_cb, + nr_turn_client_permission_error_cb, + &perm->stun))) + ABORT(r); + + /* We want to authenticate on the first packet */ + if ((r=nr_turn_stun_set_auth_params(perm->stun, ctx->realm, ctx->nonce))) + ABORT(r); + + if ((r=nr_transport_addr_copy( + &perm->stun->stun->params.permission_request.remote_addr, addr))) + ABORT(r); + STAILQ_INSERT_TAIL(&ctx->permissions, perm, entry); + + *permp = perm; + + _status=0; +abort: + if (_status) { + nr_turn_permission_destroy(&perm); + } + return(_status); +} + + +static int nr_turn_permission_find(nr_turn_client_ctx *ctx, const nr_transport_addr *addr, + nr_turn_permission **permp) +{ + nr_turn_permission *perm; + int _status; + + perm = STAILQ_FIRST(&ctx->permissions); + while (perm) { + if (!nr_transport_addr_cmp(&perm->addr, addr, + NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) + break; + + perm = STAILQ_NEXT(perm, entry); + } + + if (!perm) { + ABORT(R_NOT_FOUND); + } + if (perm->stun->last_error_code == 403) { + ABORT(R_NOT_PERMITTED); + } + *permp = perm; + + _status=0; +abort: + return(_status); +} + +static void nr_turn_client_permissions_cb(NR_SOCKET s, int how, void *arg) +{ + nr_turn_stun_ctx *ctx = (nr_turn_stun_ctx *)arg; + r_log(NR_LOG_TURN, LOG_DEBUG, "TURN(%s): Successfully refreshed permission", + ctx->tctx->label); +} + +/* Note that we don't destroy the nr_turn_stun_ctx. That is owned by the + nr_turn_client_ctx. */ +static int nr_turn_permission_destroy(nr_turn_permission **permp) +{ + nr_turn_permission *perm; + + if (!permp || !*permp) + return(0); + + perm = *permp; + *permp = 0; + + RFREE(perm); + + return(0); +} + +#endif /* USE_TURN */ diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h new file mode 100644 index 0000000000..2049f1bdfb --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/turn_client_ctx.h @@ -0,0 +1,161 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +Copyright (c) 2013, Mozilla + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance, Mozilla nor + the names of its contributors may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef _turn_client_ctx_h +#define _turn_client_ctx_h + +struct nr_ice_ctx_; + +typedef struct nr_transport_addr_listnode_ { + nr_transport_addr value; + STAILQ_ENTRY(nr_transport_addr_listnode_) entry; +} nr_transport_addr_listnode; +typedef STAILQ_HEAD(nr_transport_addr_listnode_head_, nr_transport_addr_listnode_) nr_transport_addr_listnode_head; + +/* + Represents a single set of STUN transactions, i.e., + Allocate, Refresh, Permission. It automatically handles + permissions and restarts. + */ +typedef struct nr_turn_stun_ctx_ { + struct nr_turn_client_ctx_ *tctx; + int mode; /* From stun_client_ctx.h, NR_TURN_CLIENT_MODE_* */ + int retry_ct; + nr_stun_client_ctx *stun; + char *nonce; + char *realm; + NR_async_cb success_cb; + NR_async_cb error_cb; + int last_error_code; + + nr_transport_addr_listnode_head addresses_tried; + + STAILQ_ENTRY(nr_turn_stun_ctx_) entry; +} nr_turn_stun_ctx; +typedef STAILQ_HEAD(nr_turn_stun_ctx_head_, nr_turn_stun_ctx_) + nr_turn_stun_ctx_head; + +/* Represents a single TURN permission */ +typedef struct nr_turn_permission_ { + nr_transport_addr addr; + nr_turn_stun_ctx *stun; + UINT8 last_used; + + STAILQ_ENTRY(nr_turn_permission_) entry; +} nr_turn_permission; +typedef STAILQ_HEAD(nr_turn_permission_head_, nr_turn_permission_) + nr_turn_permission_head; + +/* A single connection to a TURN server. Use one + turn_client_ctx per socket/server pair. */ +typedef struct nr_turn_client_ctx_ { + int state; +#define NR_TURN_CLIENT_STATE_INITTED 1 +#define NR_TURN_CLIENT_STATE_ALLOCATING 2 +#define NR_TURN_CLIENT_STATE_ALLOCATED 3 +#define NR_TURN_CLIENT_STATE_FAILED 4 +#define NR_TURN_CLIENT_STATE_CANCELLED 5 +#define NR_TURN_CLIENT_STATE_DEALLOCATING 6 + + char *label; + nr_socket *sock; + + char *username; + Data *password; + char *nonce; + char *realm; + + nr_transport_addr turn_server_addr; + nr_transport_addr relay_addr; + nr_transport_addr mapped_addr; + + nr_turn_stun_ctx_head stun_ctxs; + nr_turn_permission_head permissions; + + /* We need access to the socket factory to create new TCP sockets for handling + * STUN/300 responses. */ + /* If we were to require TCP nr_sockets to allow multiple connect calls by + * disconnecting and re-connecting, we could avoid this requirement. */ + struct nr_ice_ctx_* ctx; + + NR_async_cb finished_cb; + void *cb_arg; + + void *connected_timer_handle; + void *refresh_timer_handle; + + // ice telemetry + UINT2 cnt_401s; + UINT2 cnt_403s; + UINT2 cnt_438s; +} nr_turn_client_ctx; + +extern int NR_LOG_TURN; + +int nr_transport_addr_listnode_create(const nr_transport_addr *addr, nr_transport_addr_listnode **listnodep); +void nr_transport_addr_listnode_destroy(nr_transport_addr_listnode **listnode); +int nr_turn_client_ctx_create(const char* label, nr_socket* sock, + const char* username, Data* password, + nr_transport_addr* addr, + struct nr_ice_ctx_* ice_ctx, + nr_turn_client_ctx** ctxp); +int nr_turn_client_ctx_destroy(nr_turn_client_ctx **ctxp); +int nr_turn_client_allocate(nr_turn_client_ctx *ctx, + NR_async_cb finished_cb, void *cb_arg); +int nr_turn_client_get_relayed_address(nr_turn_client_ctx *ctx, + nr_transport_addr *relayed_address); +int nr_turn_client_get_mapped_address(nr_turn_client_ctx *ctx, + nr_transport_addr *mapped_address); +int nr_turn_client_process_response(nr_turn_client_ctx *ctx, + UCHAR *msg, int len, + nr_transport_addr *turn_server_addr); +int nr_turn_client_cancel(nr_turn_client_ctx *ctx); +int nr_turn_client_failed(nr_turn_client_ctx *ctx); +int nr_turn_client_deallocate(nr_turn_client_ctx *ctx); +int nr_turn_client_send_indication(nr_turn_client_ctx *ctx, + const UCHAR *msg, size_t len, + int flags, const nr_transport_addr *remote_addr); +int nr_turn_client_parse_data_indication(nr_turn_client_ctx *ctx, + nr_transport_addr *source_addr, + UCHAR *msg, size_t len, + UCHAR *newmsg, size_t *newlen, + size_t newsize, + nr_transport_addr *remote_addr); +int nr_turn_client_ensure_perm(nr_turn_client_ctx *ctx, + const nr_transport_addr *addr); +#endif + -- cgit v1.2.3