mirror of
https://github.com/coturn/coturn.git
synced 2025-11-01 23:41:09 +01:00
550 lines
14 KiB
C
550 lines
14 KiB
C
/*
|
|
* Copyright (C) 2011, 2012, 2013 Citrix Systems
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the project nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (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 "ns_turn_ioaddr.h"
|
|
#include <netdb.h>
|
|
#include <string.h>
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
uint32_t get_ioa_addr_len(const ioa_addr* addr) {
|
|
if(addr->ss.sa_family == AF_INET) return sizeof(struct sockaddr_in);
|
|
else if(addr->ss.sa_family == AF_INET6) return sizeof(struct sockaddr_in6);
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
void addr_set_any(ioa_addr *addr) {
|
|
if(addr)
|
|
bzero(addr,sizeof(ioa_addr));
|
|
}
|
|
|
|
int addr_any(const ioa_addr* addr) {
|
|
|
|
if(!addr)
|
|
return 1;
|
|
|
|
if(addr->ss.sa_family == AF_INET) {
|
|
return ((addr->s4.sin_addr.s_addr==0)&&(addr->s4.sin_port==0));
|
|
} else if(addr->ss.sa_family == AF_INET6) {
|
|
if(addr->s6.sin6_port!=0) return 0;
|
|
else {
|
|
size_t i;
|
|
for(i=0;i<sizeof(addr->s6.sin6_addr);i++)
|
|
if(((const char*)&(addr->s6.sin6_addr))[i]) return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int addr_any_no_port(const ioa_addr* addr) {
|
|
if(!addr)
|
|
return 1;
|
|
|
|
if(addr->ss.sa_family == AF_INET) {
|
|
return (addr->s4.sin_addr.s_addr==0);
|
|
} else if(addr->ss.sa_family == AF_INET6) {
|
|
size_t i;
|
|
for(i=0;i<sizeof(addr->s6.sin6_addr);i++)
|
|
if(((const char*)(&(addr->s6.sin6_addr)))[i]) return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
uint32_t hash_int32(uint32_t a)
|
|
{
|
|
a = a ^ (a>>4);
|
|
a = (a^0xdeadbeef) + (a<<5);
|
|
a = a ^ (a>>11);
|
|
return a;
|
|
}
|
|
|
|
uint64_t hash_int64(uint64_t a)
|
|
{
|
|
a = a ^ (a>>4);
|
|
a = (a^0xdeadbeefdeadbeefLL) + (a<<5);
|
|
a = a ^ (a>>11);
|
|
return a;
|
|
}
|
|
|
|
uint32_t addr_hash(const ioa_addr *addr)
|
|
{
|
|
if(!addr)
|
|
return 0;
|
|
|
|
uint32_t ret = 0;
|
|
if (addr->ss.sa_family == AF_INET) {
|
|
ret = hash_int32(addr->s4.sin_addr.s_addr + addr->s4.sin_port);
|
|
} else {
|
|
uint64_t a[2];
|
|
bcopy(&(addr->s6.sin6_addr), &a, sizeof(a));
|
|
ret = (uint32_t)((hash_int64(a[0])<<3) + (hash_int64(a[1] + addr->s6.sin6_port)));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
uint32_t addr_hash_no_port(const ioa_addr *addr)
|
|
{
|
|
if(!addr)
|
|
return 0;
|
|
|
|
uint32_t ret = 0;
|
|
if (addr->ss.sa_family == AF_INET) {
|
|
ret = hash_int32(addr->s4.sin_addr.s_addr);
|
|
} else {
|
|
uint64_t a[2];
|
|
bcopy(&(addr->s6.sin6_addr), &a, sizeof(a));
|
|
ret = (uint32_t)((hash_int64(a[0])<<3) + (hash_int64(a[1])));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void addr_cpy(ioa_addr* dst, const ioa_addr* src) {
|
|
if(dst && src)
|
|
bcopy(src,dst,sizeof(ioa_addr));
|
|
}
|
|
|
|
void addr_cpy4(ioa_addr* dst, const struct sockaddr_in* src) {
|
|
if(src && dst)
|
|
bcopy(src,dst,sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
void addr_cpy6(ioa_addr* dst, const struct sockaddr_in6* src) {
|
|
if(src && dst)
|
|
bcopy(src,dst,sizeof(struct sockaddr_in6));
|
|
}
|
|
|
|
int addr_eq(const ioa_addr* a1, const ioa_addr *a2) {
|
|
|
|
if(!a1) return (!a2);
|
|
else if(!a2) return (!a1);
|
|
|
|
if(a1->ss.sa_family == a2->ss.sa_family) {
|
|
if(a1->ss.sa_family == AF_INET && a1->s4.sin_port == a2->s4.sin_port) {
|
|
if((int)a1->s4.sin_addr.s_addr == (int)a2->s4.sin_addr.s_addr) {
|
|
return 1;
|
|
}
|
|
} else if(a1->ss.sa_family == AF_INET6 && a1->s6.sin6_port == a2->s6.sin6_port) {
|
|
if( memcmp(&(a1->s6.sin6_addr), &(a2->s6.sin6_addr) ,sizeof(struct in6_addr)) == 0 ) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int addr_eq_no_port(const ioa_addr* a1, const ioa_addr *a2) {
|
|
|
|
if(!a1) return (!a2);
|
|
else if(!a2) return (!a1);
|
|
|
|
if(a1->ss.sa_family == a2->ss.sa_family) {
|
|
if(a1->ss.sa_family == AF_INET) {
|
|
if((int)a1->s4.sin_addr.s_addr == (int)a2->s4.sin_addr.s_addr) {
|
|
return 1;
|
|
}
|
|
} else if(a1->ss.sa_family == AF_INET6) {
|
|
if( memcmp(&(a1->s6.sin6_addr), &(a2->s6.sin6_addr) ,sizeof(struct in6_addr)) == 0 ) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int make_ioa_addr(const uint8_t* saddr0, int port, ioa_addr *addr) {
|
|
|
|
if(!saddr0 || !addr) return -1;
|
|
|
|
char ssaddr[257];
|
|
STRCPY(ssaddr,saddr0);
|
|
|
|
char* saddr=ssaddr;
|
|
while(*saddr == ' ') ++saddr;
|
|
|
|
size_t len=strlen(saddr);
|
|
while(len>0) {
|
|
if(saddr[len-1]==' ') {
|
|
saddr[len-1]=0;
|
|
--len;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
bzero(addr, sizeof(ioa_addr));
|
|
if((len == 0)||
|
|
(inet_pton(AF_INET, saddr, &addr->s4.sin_addr) == 1)) {
|
|
addr->s4.sin_family = AF_INET;
|
|
#if defined(TURN_HAS_SIN_LEN) /* tested when configured */
|
|
addr->s4.sin_len = sizeof(struct sockaddr_in);
|
|
#endif
|
|
addr->s4.sin_port = nswap16(port);
|
|
} else if (inet_pton(AF_INET6, saddr, &addr->s6.sin6_addr) == 1) {
|
|
addr->s6.sin6_family = AF_INET6;
|
|
#if defined(SIN6_LEN) /* this define is required by IPv6 if used */
|
|
addr->s6.sin6_len = sizeof(struct sockaddr_in6);
|
|
#endif
|
|
addr->s6.sin6_port = nswap16(port);
|
|
} else {
|
|
struct addrinfo addr_hints;
|
|
struct addrinfo *addr_result = NULL;
|
|
int err;
|
|
|
|
memset(&addr_hints, 0, sizeof(struct addrinfo));
|
|
addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
|
addr_hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
|
|
addr_hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
|
|
addr_hints.ai_protocol = 0; /* Any protocol */
|
|
addr_hints.ai_canonname = NULL;
|
|
addr_hints.ai_addr = NULL;
|
|
addr_hints.ai_next = NULL;
|
|
|
|
err = getaddrinfo(saddr, NULL, &addr_hints, &addr_result);
|
|
if ((err != 0)||(!addr_result)) {
|
|
fprintf(stderr,"error resolving '%s' hostname: %s\n",saddr,gai_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
int family = AF_INET;
|
|
struct addrinfo *addr_result_orig = addr_result;
|
|
int found = 0;
|
|
|
|
beg_af:
|
|
|
|
while(addr_result) {
|
|
|
|
if(addr_result->ai_family == family) {
|
|
if (addr_result->ai_family == AF_INET) {
|
|
bcopy(addr_result->ai_addr, addr, addr_result->ai_addrlen);
|
|
addr->s4.sin_port = nswap16(port);
|
|
#if defined(TURN_HAS_SIN_LEN) /* tested when configured */
|
|
addr->s4.sin_len = sizeof(struct sockaddr_in);
|
|
#endif
|
|
found = 1;
|
|
break;
|
|
} else if (addr_result->ai_family == AF_INET6) {
|
|
bcopy(addr_result->ai_addr, addr, addr_result->ai_addrlen);
|
|
addr->s6.sin6_port = nswap16(port);
|
|
#if defined(SIN6_LEN) /* this define is required by IPv6 if used */
|
|
addr->s6.sin6_len = sizeof(struct sockaddr_in6);
|
|
#endif
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
addr_result = addr_result->ai_next;
|
|
}
|
|
|
|
if(!found && family == AF_INET) {
|
|
family = AF_INET6;
|
|
addr_result = addr_result_orig;
|
|
goto beg_af;
|
|
}
|
|
|
|
freeaddrinfo(addr_result_orig);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char* get_addr_string_and_port(char* s0, int *port)
|
|
{
|
|
char *s = s0;
|
|
while(*s && (*s==' ')) ++s;
|
|
if(*s == '[') {
|
|
++s;
|
|
char *tail = strstr(s,"]");
|
|
if(tail) {
|
|
*tail=0;
|
|
++tail;
|
|
while(*tail && (*tail==' ')) ++tail;
|
|
if(*tail==':') {
|
|
++tail;
|
|
*port = atoi(tail);
|
|
return s;
|
|
} else if(*tail == 0) {
|
|
*port = 0;
|
|
return s;
|
|
}
|
|
}
|
|
} else {
|
|
char *tail = strstr(s,":");
|
|
if(tail) {
|
|
*tail = 0;
|
|
++tail;
|
|
*port = atoi(tail);
|
|
return s;
|
|
} else {
|
|
*port = 0;
|
|
return s;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int make_ioa_addr_from_full_string(const uint8_t* saddr, int default_port, ioa_addr *addr)
|
|
{
|
|
if(!addr)
|
|
return -1;
|
|
|
|
int ret = -1;
|
|
int port = 0;
|
|
char* s = strdup((const char*)saddr);
|
|
char *sa = get_addr_string_and_port(s,&port);
|
|
if(sa) {
|
|
if(port<1)
|
|
port = default_port;
|
|
ret = make_ioa_addr((uint8_t*)sa,port,addr);
|
|
}
|
|
free(s);
|
|
return ret;
|
|
}
|
|
|
|
int addr_to_string(const ioa_addr* addr, uint8_t* saddr)
|
|
{
|
|
|
|
if (addr && saddr) {
|
|
|
|
char addrtmp[INET6_ADDRSTRLEN];
|
|
|
|
if (addr->ss.sa_family == AF_INET) {
|
|
inet_ntop(AF_INET, &addr->s4.sin_addr, addrtmp, INET_ADDRSTRLEN);
|
|
if(addr_get_port(addr)>0)
|
|
snprintf((char*)saddr, MAX_IOA_ADDR_STRING, "%s:%d", addrtmp, addr_get_port(addr));
|
|
else
|
|
strncpy((char*)saddr, addrtmp, MAX_IOA_ADDR_STRING);
|
|
} else if (addr->ss.sa_family == AF_INET6) {
|
|
inet_ntop(AF_INET6, &addr->s6.sin6_addr, addrtmp, INET6_ADDRSTRLEN);
|
|
if(addr_get_port(addr)>0)
|
|
snprintf((char*)saddr, MAX_IOA_ADDR_STRING, "[%s]:%d", addrtmp, addr_get_port(addr));
|
|
else
|
|
strncpy((char*)saddr, addrtmp, MAX_IOA_ADDR_STRING);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int addr_to_string_no_port(const ioa_addr* addr, uint8_t* saddr)
|
|
{
|
|
|
|
if (addr && saddr) {
|
|
|
|
char addrtmp[MAX_IOA_ADDR_STRING];
|
|
|
|
if (addr->ss.sa_family == AF_INET) {
|
|
inet_ntop(AF_INET, &addr->s4.sin_addr, addrtmp, INET_ADDRSTRLEN);
|
|
strncpy((char*)saddr, addrtmp, MAX_IOA_ADDR_STRING);
|
|
} else if (addr->ss.sa_family == AF_INET6) {
|
|
inet_ntop(AF_INET6, &addr->s6.sin6_addr, addrtmp, INET6_ADDRSTRLEN);
|
|
strncpy((char*)saddr, addrtmp, MAX_IOA_ADDR_STRING);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void addr_set_port(ioa_addr* addr, int port) {
|
|
if(addr) {
|
|
if(addr->s4.sin_family == AF_INET) {
|
|
addr->s4.sin_port = nswap16(port);
|
|
} else if(addr->s6.sin6_family == AF_INET6) {
|
|
addr->s6.sin6_port = nswap16(port);
|
|
}
|
|
}
|
|
}
|
|
|
|
int addr_get_port(const ioa_addr* addr) {
|
|
if(!addr)
|
|
return 0;
|
|
|
|
if(addr->s4.sin_family == AF_INET) {
|
|
return nswap16(addr->s4.sin_port);
|
|
} else if(addr->s6.sin6_family == AF_INET6) {
|
|
return nswap16(addr->s6.sin6_port);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ioa_addr_range_set(ioa_addr_range* range, const ioa_addr* addr_min, const ioa_addr* addr_max) {
|
|
if(range) {
|
|
if(addr_min) addr_cpy(&(range->min),addr_min);
|
|
else addr_set_any(&(range->min));
|
|
if(addr_max) addr_cpy(&(range->max),addr_max);
|
|
else addr_set_any(&(range->max));
|
|
}
|
|
}
|
|
|
|
int addr_less_eq(const ioa_addr* addr1, const ioa_addr* addr2) {
|
|
|
|
if(!addr1) return 1;
|
|
else if(!addr2) return 0;
|
|
else {
|
|
if(addr1->ss.sa_family != addr2->ss.sa_family) return (addr1->ss.sa_family < addr2->ss.sa_family);
|
|
else if(addr1->ss.sa_family == AF_INET) {
|
|
return ((uint32_t)nswap32(addr1->s4.sin_addr.s_addr) <= (uint32_t)nswap32(addr2->s4.sin_addr.s_addr));
|
|
} else if(addr1->ss.sa_family == AF_INET6) {
|
|
int i;
|
|
for(i=0;i<16;i++) {
|
|
if((uint8_t)(((const char*)&(addr1->s6.sin6_addr))[i]) > (uint8_t)(((const char*)&(addr2->s6.sin6_addr))[i]))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
} else return 1;
|
|
}
|
|
}
|
|
|
|
int ioa_addr_in_range(const ioa_addr_range* range, const ioa_addr* addr) {
|
|
|
|
if(range && addr) {
|
|
if(addr_any(&(range->min)) || addr_less_eq(&(range->min),addr)) {
|
|
if(addr_any(&(range->max))) {
|
|
return 1;
|
|
} else {
|
|
return addr_less_eq(addr,&(range->max));
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ioa_addr_range_cpy(ioa_addr_range* dest, const ioa_addr_range* src) {
|
|
if(dest && src) {
|
|
addr_cpy(&(dest->min),&(src->min));
|
|
addr_cpy(&(dest->max),&(src->max));
|
|
}
|
|
}
|
|
|
|
/////// Check whether this is a good address //////////////
|
|
|
|
int ioa_addr_is_multicast(ioa_addr *addr)
|
|
{
|
|
if(addr) {
|
|
if(addr->ss.sa_family == AF_INET) {
|
|
const uint8_t *u = ((const uint8_t*)&(addr->s4.sin_addr));
|
|
return (u[0] > 223);
|
|
} else if(addr->ss.sa_family == AF_INET6) {
|
|
uint8_t u = ((const uint8_t*)&(addr->s6.sin6_addr))[0];
|
|
return (u == 255);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ioa_addr_is_loopback(ioa_addr *addr)
|
|
{
|
|
if(addr) {
|
|
if(addr->ss.sa_family == AF_INET) {
|
|
const uint8_t *u = ((const uint8_t*)&(addr->s4.sin_addr));
|
|
return (u[0] == 127);
|
|
} else if(addr->ss.sa_family == AF_INET6) {
|
|
const uint8_t *u = ((const uint8_t*)&(addr->s6.sin6_addr));
|
|
if(u[7] == 1) {
|
|
int i;
|
|
for(i=0;i<7;++i) {
|
|
if(u[i])
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/////// Map "public" address to "private" address //////////////
|
|
|
|
// Must be called only in a single-threaded context,
|
|
// before the program starts spawning threads:
|
|
|
|
static ioa_addr **public_addrs = NULL;
|
|
static ioa_addr **private_addrs = NULL;
|
|
static size_t mcount = 0;
|
|
static size_t msz = 0;
|
|
|
|
void ioa_addr_add_mapping(ioa_addr *apub, ioa_addr *apriv)
|
|
{
|
|
size_t new_size = msz + sizeof(ioa_addr*);
|
|
public_addrs = (ioa_addr**)realloc(public_addrs, new_size);
|
|
private_addrs = (ioa_addr**)realloc(private_addrs, new_size);
|
|
public_addrs[mcount]=(ioa_addr*)malloc(sizeof(ioa_addr));
|
|
private_addrs[mcount]=(ioa_addr*)malloc(sizeof(ioa_addr));
|
|
addr_cpy(public_addrs[mcount],apub);
|
|
addr_cpy(private_addrs[mcount],apriv);
|
|
++mcount;
|
|
msz += sizeof(ioa_addr*);
|
|
}
|
|
|
|
void map_addr_from_public_to_private(const ioa_addr *public_addr, ioa_addr *private_addr)
|
|
{
|
|
size_t i;
|
|
for(i=0;i<mcount;++i) {
|
|
if(addr_eq_no_port(public_addr,public_addrs[i])) {
|
|
addr_cpy(private_addr,private_addrs[i]);
|
|
addr_set_port(private_addr,addr_get_port(public_addr));
|
|
return;
|
|
}
|
|
}
|
|
addr_cpy(private_addr,public_addr);
|
|
}
|
|
|
|
void map_addr_from_private_to_public(const ioa_addr *private_addr, ioa_addr *public_addr)
|
|
{
|
|
size_t i;
|
|
for(i=0;i<mcount;++i) {
|
|
if(addr_eq_no_port(private_addr,private_addrs[i])) {
|
|
addr_cpy(public_addr,public_addrs[i]);
|
|
addr_set_port(public_addr,addr_get_port(private_addr));
|
|
return;
|
|
}
|
|
}
|
|
addr_cpy(public_addr,private_addr);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|