| /* $Id$ */ |
| /* |
| * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| #include <pj/sock.h> |
| #include <pj/assert.h> |
| #include <pj/ctype.h> |
| #include <pj/errno.h> |
| #include <pj/ip_helper.h> |
| #include <pj/os.h> |
| #include <pj/addr_resolv.h> |
| #include <pj/rand.h> |
| #include <pj/string.h> |
| #include <pj/compat/socket.h> |
| |
| #if 0 |
| /* Enable some tracing */ |
| #include <pj/log.h> |
| #define THIS_FILE "sock_common.c" |
| #define TRACE_(arg) PJ_LOG(4,arg) |
| #else |
| #define TRACE_(arg) |
| #endif |
| |
| |
| /* |
| * Convert address string with numbers and dots to binary IP address. |
| */ |
| PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) |
| { |
| pj_in_addr addr; |
| |
| pj_inet_aton(cp, &addr); |
| return addr; |
| } |
| |
| /* |
| * Convert address string with numbers and dots to binary IP address. |
| */ |
| PJ_DEF(pj_in_addr) pj_inet_addr2(const char *cp) |
| { |
| pj_str_t str = pj_str((char*)cp); |
| return pj_inet_addr(&str); |
| } |
| |
| /* |
| * Get text representation. |
| */ |
| PJ_DEF(char*) pj_inet_ntop2( int af, const void *src, |
| char *dst, int size) |
| { |
| pj_status_t status; |
| |
| status = pj_inet_ntop(af, src, dst, size); |
| return (status==PJ_SUCCESS)? dst : NULL; |
| } |
| |
| /* |
| * Print socket address. |
| */ |
| PJ_DEF(char*) pj_sockaddr_print( const pj_sockaddr_t *addr, |
| char *buf, int size, |
| unsigned flags) |
| { |
| enum { |
| WITH_PORT = 1, |
| WITH_BRACKETS = 2 |
| }; |
| |
| char txt[PJ_INET6_ADDRSTRLEN]; |
| char port[32]; |
| const pj_addr_hdr *h = (const pj_addr_hdr*)addr; |
| char *bquote, *equote; |
| pj_status_t status; |
| |
| status = pj_inet_ntop(h->sa_family, pj_sockaddr_get_addr(addr), |
| txt, sizeof(txt)); |
| if (status != PJ_SUCCESS) |
| return ""; |
| |
| if (h->sa_family != PJ_AF_INET6 || (flags & WITH_BRACKETS)==0) { |
| bquote = ""; equote = ""; |
| } else { |
| bquote = "["; equote = "]"; |
| } |
| |
| if (flags & WITH_PORT) { |
| pj_ansi_snprintf(port, sizeof(port), ":%d", |
| pj_sockaddr_get_port(addr)); |
| } else { |
| port[0] = '\0'; |
| } |
| |
| pj_ansi_snprintf(buf, size, "%s%s%s%s", |
| bquote, txt, equote, port); |
| |
| return buf; |
| } |
| |
| /* |
| * Set the IP address of an IP socket address from string address, |
| * with resolving the host if necessary. The string address may be in a |
| * standard numbers and dots notation or may be a hostname. If hostname |
| * is specified, then the function will resolve the host into the IP |
| * address. |
| */ |
| PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, |
| const pj_str_t *str_addr) |
| { |
| PJ_CHECK_STACK(); |
| |
| PJ_ASSERT_RETURN(!str_addr || str_addr->slen < PJ_MAX_HOSTNAME, |
| (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); |
| |
| PJ_SOCKADDR_RESET_LEN(addr); |
| addr->sin_family = AF_INET; |
| pj_bzero(addr->sin_zero, sizeof(addr->sin_zero)); |
| |
| if (str_addr && str_addr->slen) { |
| addr->sin_addr = pj_inet_addr(str_addr); |
| if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { |
| pj_hostent he; |
| pj_status_t rc; |
| |
| rc = pj_gethostbyname(str_addr, &he); |
| if (rc == 0) { |
| addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; |
| } else { |
| addr->sin_addr.s_addr = PJ_INADDR_NONE; |
| return rc; |
| } |
| } |
| |
| } else { |
| addr->sin_addr.s_addr = 0; |
| } |
| |
| return PJ_SUCCESS; |
| } |
| |
| /* Set address from a name */ |
| PJ_DEF(pj_status_t) pj_sockaddr_set_str_addr(int af, |
| pj_sockaddr *addr, |
| const pj_str_t *str_addr) |
| { |
| pj_status_t status; |
| |
| if (af == PJ_AF_INET) { |
| return pj_sockaddr_in_set_str_addr(&addr->ipv4, str_addr); |
| } |
| |
| PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP); |
| |
| /* IPv6 specific */ |
| |
| addr->ipv6.sin6_family = PJ_AF_INET6; |
| PJ_SOCKADDR_RESET_LEN(addr); |
| |
| if (str_addr && str_addr->slen) { |
| status = pj_inet_pton(PJ_AF_INET6, str_addr, &addr->ipv6.sin6_addr); |
| if (status != PJ_SUCCESS) { |
| pj_addrinfo ai; |
| unsigned count = 1; |
| |
| status = pj_getaddrinfo(PJ_AF_INET6, str_addr, &count, &ai); |
| if (status==PJ_SUCCESS) { |
| pj_memcpy(&addr->ipv6.sin6_addr, &ai.ai_addr.ipv6.sin6_addr, |
| sizeof(pj_sockaddr_in6)); |
| } |
| } |
| } else { |
| status = PJ_SUCCESS; |
| } |
| |
| return status; |
| } |
| |
| /* |
| * Set the IP address and port of an IP socket address. |
| * The string address may be in a standard numbers and dots notation or |
| * may be a hostname. If hostname is specified, then the function will |
| * resolve the host into the IP address. |
| */ |
| PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, |
| const pj_str_t *str_addr, |
| pj_uint16_t port) |
| { |
| PJ_ASSERT_RETURN(addr, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); |
| |
| PJ_SOCKADDR_RESET_LEN(addr); |
| addr->sin_family = PJ_AF_INET; |
| pj_bzero(addr->sin_zero, sizeof(addr->sin_zero)); |
| pj_sockaddr_in_set_port(addr, port); |
| return pj_sockaddr_in_set_str_addr(addr, str_addr); |
| } |
| |
| /* |
| * Initialize IP socket address based on the address and port info. |
| */ |
| PJ_DEF(pj_status_t) pj_sockaddr_init(int af, |
| pj_sockaddr *addr, |
| const pj_str_t *cp, |
| pj_uint16_t port) |
| { |
| pj_status_t status; |
| |
| if (af == PJ_AF_INET) { |
| return pj_sockaddr_in_init(&addr->ipv4, cp, port); |
| } |
| |
| /* IPv6 specific */ |
| PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP); |
| |
| pj_bzero(addr, sizeof(pj_sockaddr_in6)); |
| addr->addr.sa_family = PJ_AF_INET6; |
| |
| status = pj_sockaddr_set_str_addr(af, addr, cp); |
| if (status != PJ_SUCCESS) |
| return status; |
| |
| addr->ipv6.sin6_port = pj_htons(port); |
| return PJ_SUCCESS; |
| } |
| |
| /* |
| * Compare two socket addresses. |
| */ |
| PJ_DEF(int) pj_sockaddr_cmp( const pj_sockaddr_t *addr1, |
| const pj_sockaddr_t *addr2) |
| { |
| const pj_sockaddr *a1 = (const pj_sockaddr*) addr1; |
| const pj_sockaddr *a2 = (const pj_sockaddr*) addr2; |
| int port1, port2; |
| int result; |
| |
| /* Compare address family */ |
| if (a1->addr.sa_family < a2->addr.sa_family) |
| return -1; |
| else if (a1->addr.sa_family > a2->addr.sa_family) |
| return 1; |
| |
| /* Compare addresses */ |
| result = pj_memcmp(pj_sockaddr_get_addr(a1), |
| pj_sockaddr_get_addr(a2), |
| pj_sockaddr_get_addr_len(a1)); |
| if (result != 0) |
| return result; |
| |
| /* Compare port number */ |
| port1 = pj_sockaddr_get_port(a1); |
| port2 = pj_sockaddr_get_port(a2); |
| |
| if (port1 < port2) |
| return -1; |
| else if (port1 > port2) |
| return 1; |
| |
| /* TODO: |
| * Do we need to compare flow label and scope id in IPv6? |
| */ |
| |
| /* Looks equal */ |
| return 0; |
| } |
| |
| /* |
| * Get first IP address associated with the hostname. |
| */ |
| PJ_DEF(pj_in_addr) pj_gethostaddr(void) |
| { |
| pj_sockaddr_in addr; |
| const pj_str_t *hostname = pj_gethostname(); |
| |
| pj_sockaddr_in_set_str_addr(&addr, hostname); |
| return addr.sin_addr; |
| } |
| |
| /* |
| * Get port number of a pj_sockaddr_in |
| */ |
| PJ_DEF(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr) |
| { |
| return pj_ntohs(addr->sin_port); |
| } |
| |
| /* |
| * Get the address part |
| */ |
| PJ_DEF(void*) pj_sockaddr_get_addr(const pj_sockaddr_t *addr) |
| { |
| const pj_sockaddr *a = (const pj_sockaddr*)addr; |
| |
| PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || |
| a->addr.sa_family == PJ_AF_INET6, NULL); |
| |
| if (a->addr.sa_family == PJ_AF_INET6) |
| return (void*) &a->ipv6.sin6_addr; |
| else |
| return (void*) &a->ipv4.sin_addr; |
| } |
| |
| /* |
| * Check if sockaddr contains a non-zero address |
| */ |
| PJ_DEF(pj_bool_t) pj_sockaddr_has_addr(const pj_sockaddr_t *addr) |
| { |
| const pj_sockaddr *a = (const pj_sockaddr*)addr; |
| |
| /* It's probably not wise to raise assertion here if |
| * the address doesn't contain a valid address family, and |
| * just return PJ_FALSE instead. |
| * |
| * The reason is because application may need to distinguish |
| * these three conditions with sockaddr: |
| * a) sockaddr is not initialized. This is by convention |
| * indicated by sa_family==0. |
| * b) sockaddr is initialized with zero address. This is |
| * indicated with the address field having zero address. |
| * c) sockaddr is initialized with valid address/port. |
| * |
| * If we enable this assertion, then application will loose |
| * the capability to specify condition a), since it will be |
| * forced to always initialize sockaddr (even with zero address). |
| * This may break some parts of upper layer libraries. |
| */ |
| //PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || |
| // a->addr.sa_family == PJ_AF_INET6, PJ_FALSE); |
| |
| if (a->addr.sa_family!=PJ_AF_INET && a->addr.sa_family!=PJ_AF_INET6) { |
| return PJ_FALSE; |
| } else if (a->addr.sa_family == PJ_AF_INET6) { |
| pj_uint8_t zero[24]; |
| pj_bzero(zero, sizeof(zero)); |
| return pj_memcmp(a->ipv6.sin6_addr.s6_addr, zero, |
| sizeof(pj_in6_addr)) != 0; |
| } else |
| return a->ipv4.sin_addr.s_addr != PJ_INADDR_ANY; |
| } |
| |
| /* |
| * Get port number |
| */ |
| PJ_DEF(pj_uint16_t) pj_sockaddr_get_port(const pj_sockaddr_t *addr) |
| { |
| const pj_sockaddr *a = (const pj_sockaddr*) addr; |
| |
| PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || |
| a->addr.sa_family == PJ_AF_INET6, (pj_uint16_t)0xFFFF); |
| |
| return pj_ntohs((pj_uint16_t)(a->addr.sa_family == PJ_AF_INET6 ? |
| a->ipv6.sin6_port : a->ipv4.sin_port)); |
| } |
| |
| /* |
| * Get the length of the address part. |
| */ |
| PJ_DEF(unsigned) pj_sockaddr_get_addr_len(const pj_sockaddr_t *addr) |
| { |
| const pj_sockaddr *a = (const pj_sockaddr*) addr; |
| PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || |
| a->addr.sa_family == PJ_AF_INET6, 0); |
| return a->addr.sa_family == PJ_AF_INET6 ? |
| sizeof(pj_in6_addr) : sizeof(pj_in_addr); |
| } |
| |
| /* |
| * Get socket address length. |
| */ |
| PJ_DEF(unsigned) pj_sockaddr_get_len(const pj_sockaddr_t *addr) |
| { |
| const pj_sockaddr *a = (const pj_sockaddr*) addr; |
| PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET || |
| a->addr.sa_family == PJ_AF_INET6, 0); |
| return a->addr.sa_family == PJ_AF_INET6 ? |
| sizeof(pj_sockaddr_in6) : sizeof(pj_sockaddr_in); |
| } |
| |
| /* |
| * Copy only the address part (sin_addr/sin6_addr) of a socket address. |
| */ |
| PJ_DEF(void) pj_sockaddr_copy_addr( pj_sockaddr *dst, |
| const pj_sockaddr *src) |
| { |
| /* Destination sockaddr might not be initialized */ |
| const char *srcbuf = (char*)pj_sockaddr_get_addr(src); |
| char *dstbuf = ((char*)dst) + (srcbuf - (char*)src); |
| pj_memcpy(dstbuf, srcbuf, pj_sockaddr_get_addr_len(src)); |
| } |
| |
| /* |
| * Copy socket address. |
| */ |
| PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src) |
| { |
| pj_memcpy(dst, src, pj_sockaddr_get_len(src)); |
| } |
| |
| /* |
| * Set port number of pj_sockaddr_in |
| */ |
| PJ_DEF(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, |
| pj_uint16_t hostport) |
| { |
| addr->sin_port = pj_htons(hostport); |
| } |
| |
| /* |
| * Set port number of pj_sockaddr |
| */ |
| PJ_DEF(pj_status_t) pj_sockaddr_set_port(pj_sockaddr *addr, |
| pj_uint16_t hostport) |
| { |
| int af = addr->addr.sa_family; |
| |
| PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL); |
| |
| if (af == PJ_AF_INET6) |
| addr->ipv6.sin6_port = pj_htons(hostport); |
| else |
| addr->ipv4.sin_port = pj_htons(hostport); |
| |
| return PJ_SUCCESS; |
| } |
| |
| /* |
| * Get IPv4 address |
| */ |
| PJ_DEF(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr) |
| { |
| pj_in_addr in_addr; |
| in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr); |
| return in_addr; |
| } |
| |
| /* |
| * Set IPv4 address |
| */ |
| PJ_DEF(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, |
| pj_uint32_t hostaddr) |
| { |
| addr->sin_addr.s_addr = pj_htonl(hostaddr); |
| } |
| |
| /* |
| * Parse address |
| */ |
| PJ_DEF(pj_status_t) pj_sockaddr_parse2(int af, unsigned options, |
| const pj_str_t *str, |
| pj_str_t *p_hostpart, |
| pj_uint16_t *p_port, |
| int *raf) |
| { |
| const char *end = str->ptr + str->slen; |
| const char *last_colon_pos = NULL; |
| unsigned colon_cnt = 0; |
| const char *p; |
| |
| PJ_ASSERT_RETURN((af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && |
| options==0 && |
| str!=NULL, PJ_EINVAL); |
| |
| /* Special handling for empty input */ |
| if (str->slen==0 || str->ptr==NULL) { |
| if (p_hostpart) |
| p_hostpart->slen = 0; |
| if (p_port) |
| *p_port = 0; |
| if (raf) |
| *raf = PJ_AF_INET; |
| return PJ_SUCCESS; |
| } |
| |
| /* Count the colon and get the last colon */ |
| for (p=str->ptr; p!=end; ++p) { |
| if (*p == ':') { |
| ++colon_cnt; |
| last_colon_pos = p; |
| } |
| } |
| |
| /* Deduce address family if it's not given */ |
| if (af == PJ_AF_UNSPEC) { |
| if (colon_cnt > 1) |
| af = PJ_AF_INET6; |
| else |
| af = PJ_AF_INET; |
| } else if (af == PJ_AF_INET && colon_cnt > 1) |
| return PJ_EINVAL; |
| |
| if (raf) |
| *raf = af; |
| |
| if (af == PJ_AF_INET) { |
| /* Parse as IPv4. Supported formats: |
| * - "10.0.0.1:80" |
| * - "10.0.0.1" |
| * - "10.0.0.1:" |
| * - ":80" |
| * - ":" |
| */ |
| pj_str_t hostpart; |
| unsigned long port; |
| |
| hostpart.ptr = (char*)str->ptr; |
| |
| if (last_colon_pos) { |
| pj_str_t port_part; |
| int i; |
| |
| hostpart.slen = last_colon_pos - str->ptr; |
| |
| port_part.ptr = (char*)last_colon_pos + 1; |
| port_part.slen = end - port_part.ptr; |
| |
| /* Make sure port number is valid */ |
| for (i=0; i<port_part.slen; ++i) { |
| if (!pj_isdigit(port_part.ptr[i])) |
| return PJ_EINVAL; |
| } |
| port = pj_strtoul(&port_part); |
| if (port > 65535) |
| return PJ_EINVAL; |
| } else { |
| hostpart.slen = str->slen; |
| port = 0; |
| } |
| |
| if (p_hostpart) |
| *p_hostpart = hostpart; |
| if (p_port) |
| *p_port = (pj_uint16_t)port; |
| |
| return PJ_SUCCESS; |
| |
| } else if (af == PJ_AF_INET6) { |
| |
| /* Parse as IPv6. Supported formats: |
| * - "fe::01:80" ==> note: port number is zero in this case, not 80! |
| * - "[fe::01]:80" |
| * - "fe::01" |
| * - "fe::01:" |
| * - "[fe::01]" |
| * - "[fe::01]:" |
| * - "[::]:80" |
| * - ":::80" |
| * - "[::]" |
| * - "[::]:" |
| * - ":::" |
| * - "::" |
| */ |
| pj_str_t hostpart, port_part; |
| |
| if (*str->ptr == '[') { |
| char *end_bracket; |
| int i; |
| unsigned long port; |
| |
| if (last_colon_pos == NULL) |
| return PJ_EINVAL; |
| |
| end_bracket = pj_strchr(str, ']'); |
| if (end_bracket == NULL) |
| return PJ_EINVAL; |
| |
| hostpart.ptr = (char*)str->ptr + 1; |
| hostpart.slen = end_bracket - hostpart.ptr; |
| |
| if (last_colon_pos < end_bracket) { |
| port_part.ptr = NULL; |
| port_part.slen = 0; |
| } else { |
| port_part.ptr = (char*)last_colon_pos + 1; |
| port_part.slen = end - port_part.ptr; |
| } |
| |
| /* Make sure port number is valid */ |
| for (i=0; i<port_part.slen; ++i) { |
| if (!pj_isdigit(port_part.ptr[i])) |
| return PJ_EINVAL; |
| } |
| port = pj_strtoul(&port_part); |
| if (port > 65535) |
| return PJ_EINVAL; |
| |
| if (p_hostpart) |
| *p_hostpart = hostpart; |
| if (p_port) |
| *p_port = (pj_uint16_t)port; |
| |
| return PJ_SUCCESS; |
| |
| } else { |
| /* Treat everything as part of the IPv6 IP address */ |
| if (p_hostpart) |
| *p_hostpart = *str; |
| if (p_port) |
| *p_port = 0; |
| |
| return PJ_SUCCESS; |
| } |
| |
| } else { |
| return PJ_EAFNOTSUP; |
| } |
| |
| } |
| |
| /* |
| * Parse address |
| */ |
| PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options, |
| const pj_str_t *str, |
| pj_sockaddr *addr) |
| { |
| pj_str_t hostpart; |
| pj_uint16_t port; |
| pj_status_t status; |
| |
| PJ_ASSERT_RETURN(addr, PJ_EINVAL); |
| PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || |
| af==PJ_AF_INET || |
| af==PJ_AF_INET6, PJ_EINVAL); |
| PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); |
| |
| status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af); |
| if (status != PJ_SUCCESS) |
| return status; |
| |
| #if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6 |
| if (af==PJ_AF_INET6) |
| return PJ_EIPV6NOTSUP; |
| #endif |
| |
| status = pj_sockaddr_init(af, addr, &hostpart, port); |
| #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 |
| if (status != PJ_SUCCESS && af == PJ_AF_INET6) { |
| /* Parsing does not yield valid address. Try to treat the last |
| * portion after the colon as port number. |
| */ |
| const char *last_colon_pos=NULL, *p; |
| const char *end = str->ptr + str->slen; |
| unsigned long long_port; |
| pj_str_t port_part; |
| int i; |
| |
| /* Parse as IPv6:port */ |
| for (p=str->ptr; p!=end; ++p) { |
| if (*p == ':') |
| last_colon_pos = p; |
| } |
| |
| if (last_colon_pos == NULL) |
| return status; |
| |
| hostpart.ptr = (char*)str->ptr; |
| hostpart.slen = last_colon_pos - str->ptr; |
| |
| port_part.ptr = (char*)last_colon_pos + 1; |
| port_part.slen = end - port_part.ptr; |
| |
| /* Make sure port number is valid */ |
| for (i=0; i<port_part.slen; ++i) { |
| if (!pj_isdigit(port_part.ptr[i])) |
| return status; |
| } |
| long_port = pj_strtoul(&port_part); |
| if (long_port > 65535) |
| return status; |
| |
| port = (pj_uint16_t)long_port; |
| |
| status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port); |
| } |
| #endif |
| |
| return status; |
| } |
| |
| /* Resolve the IP address of local machine */ |
| PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr) |
| { |
| unsigned i, count, cand_cnt; |
| enum { |
| CAND_CNT = 8, |
| |
| /* Weighting to be applied to found addresses */ |
| WEIGHT_HOSTNAME = 1, /* hostname IP is not always valid! */ |
| WEIGHT_DEF_ROUTE = 2, |
| WEIGHT_INTERFACE = 1, |
| WEIGHT_LOOPBACK = -5, |
| WEIGHT_LINK_LOCAL = -4, |
| WEIGHT_DISABLED = -50, |
| |
| MIN_WEIGHT = WEIGHT_DISABLED+1 /* minimum weight to use */ |
| }; |
| /* candidates: */ |
| pj_sockaddr cand_addr[CAND_CNT]; |
| int cand_weight[CAND_CNT]; |
| int selected_cand; |
| char strip[PJ_INET6_ADDRSTRLEN+10]; |
| /* Special IPv4 addresses. */ |
| struct spec_ipv4_t |
| { |
| pj_uint32_t addr; |
| pj_uint32_t mask; |
| int weight; |
| } spec_ipv4[] = |
| { |
| /* 127.0.0.0/8, loopback addr will be used if there is no other |
| * addresses. |
| */ |
| { 0x7f000000, 0xFF000000, WEIGHT_LOOPBACK }, |
| |
| /* 0.0.0.0/8, special IP that doesn't seem to be practically useful */ |
| { 0x00000000, 0xFF000000, WEIGHT_DISABLED }, |
| |
| /* 169.254.0.0/16, a zeroconf/link-local address, which has higher |
| * priority than loopback and will be used if there is no other |
| * valid addresses. |
| */ |
| { 0xa9fe0000, 0xFFFF0000, WEIGHT_LINK_LOCAL } |
| }; |
| /* Special IPv6 addresses */ |
| struct spec_ipv6_t |
| { |
| pj_uint8_t addr[16]; |
| pj_uint8_t mask[16]; |
| int weight; |
| } spec_ipv6[] = |
| { |
| /* Loopback address, ::1/128 */ |
| { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, |
| {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, |
| WEIGHT_LOOPBACK |
| }, |
| |
| /* Link local, fe80::/10 */ |
| { {0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
| {0xff,0xc0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
| WEIGHT_LINK_LOCAL |
| }, |
| |
| /* Disabled, ::/128 */ |
| { {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, |
| { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}, |
| WEIGHT_DISABLED |
| } |
| }; |
| pj_addrinfo ai; |
| pj_status_t status; |
| |
| /* May not be used if TRACE_ is disabled */ |
| PJ_UNUSED_ARG(strip); |
| |
| #ifdef _MSC_VER |
| /* Get rid of "uninitialized he variable" with MS compilers */ |
| pj_bzero(&ai, sizeof(ai)); |
| #endif |
| |
| cand_cnt = 0; |
| pj_bzero(cand_addr, sizeof(cand_addr)); |
| pj_bzero(cand_weight, sizeof(cand_weight)); |
| for (i=0; i<PJ_ARRAY_SIZE(cand_addr); ++i) { |
| cand_addr[i].addr.sa_family = (pj_uint16_t)af; |
| PJ_SOCKADDR_RESET_LEN(&cand_addr[i]); |
| } |
| |
| addr->addr.sa_family = (pj_uint16_t)af; |
| PJ_SOCKADDR_RESET_LEN(addr); |
| |
| #if !defined(PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION) || \ |
| PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION == 0 |
| /* Get hostname's IP address */ |
| count = 1; |
| status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai); |
| if (status == PJ_SUCCESS) { |
| pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af); |
| pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr); |
| pj_sockaddr_set_port(&cand_addr[cand_cnt], 0); |
| cand_weight[cand_cnt] += WEIGHT_HOSTNAME; |
| ++cand_cnt; |
| |
| TRACE_((THIS_FILE, "hostname IP is %s", |
| pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0))); |
| } |
| #else |
| PJ_UNUSED_ARG(ai); |
| PJ_UNUSED_ARG(count); |
| #endif |
| |
| /* Get default interface (interface for default route) */ |
| if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { |
| status = pj_getdefaultipinterface(af, addr); |
| if (status == PJ_SUCCESS) { |
| TRACE_((THIS_FILE, "default IP is %s", |
| pj_sockaddr_print(addr, strip, sizeof(strip), 0))); |
| |
| pj_sockaddr_set_port(addr, 0); |
| for (i=0; i<cand_cnt; ++i) { |
| if (pj_sockaddr_cmp(&cand_addr[i], addr)==0) |
| break; |
| } |
| |
| cand_weight[i] += WEIGHT_DEF_ROUTE; |
| if (i >= cand_cnt) { |
| pj_sockaddr_copy_addr(&cand_addr[i], addr); |
| ++cand_cnt; |
| } |
| } |
| } |
| |
| |
| /* Enumerate IP interfaces */ |
| if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) { |
| unsigned start_if = cand_cnt; |
| unsigned count = PJ_ARRAY_SIZE(cand_addr) - start_if; |
| |
| status = pj_enum_ip_interface(af, &count, &cand_addr[start_if]); |
| if (status == PJ_SUCCESS && count) { |
| /* Clear the port number */ |
| for (i=0; i<count; ++i) |
| pj_sockaddr_set_port(&cand_addr[start_if+i], 0); |
| |
| /* For each candidate that we found so far (that is the hostname |
| * address and default interface address, check if they're found |
| * in the interface list. If found, add the weight, and if not, |
| * decrease the weight. |
| */ |
| for (i=0; i<cand_cnt; ++i) { |
| unsigned j; |
| for (j=0; j<count; ++j) { |
| if (pj_sockaddr_cmp(&cand_addr[i], |
| &cand_addr[start_if+j])==0) |
| break; |
| } |
| |
| if (j == count) { |
| /* Not found */ |
| cand_weight[i] -= WEIGHT_INTERFACE; |
| } else { |
| cand_weight[i] += WEIGHT_INTERFACE; |
| } |
| } |
| |
| /* Add remaining interface to candidate list. */ |
| for (i=0; i<count; ++i) { |
| unsigned j; |
| for (j=0; j<cand_cnt; ++j) { |
| if (pj_sockaddr_cmp(&cand_addr[start_if+i], |
| &cand_addr[j])==0) |
| break; |
| } |
| |
| if (j == cand_cnt) { |
| pj_sockaddr_copy_addr(&cand_addr[cand_cnt], |
| &cand_addr[start_if+i]); |
| cand_weight[cand_cnt] += WEIGHT_INTERFACE; |
| ++cand_cnt; |
| } |
| } |
| } |
| } |
| |
| /* Apply weight adjustment for special IPv4/IPv6 addresses |
| * See http://trac.pjsip.org/repos/ticket/1046 |
| */ |
| if (af == PJ_AF_INET) { |
| for (i=0; i<cand_cnt; ++i) { |
| unsigned j; |
| for (j=0; j<PJ_ARRAY_SIZE(spec_ipv4); ++j) { |
| pj_uint32_t a = pj_ntohl(cand_addr[i].ipv4.sin_addr.s_addr); |
| pj_uint32_t pa = spec_ipv4[j].addr; |
| pj_uint32_t pm = spec_ipv4[j].mask; |
| |
| if ((a & pm) == pa) { |
| cand_weight[i] += spec_ipv4[j].weight; |
| break; |
| } |
| } |
| } |
| } else if (af == PJ_AF_INET6) { |
| for (i=0; i<PJ_ARRAY_SIZE(spec_ipv6); ++i) { |
| unsigned j; |
| for (j=0; j<cand_cnt; ++j) { |
| pj_uint8_t *a = cand_addr[j].ipv6.sin6_addr.s6_addr; |
| pj_uint8_t am[16]; |
| pj_uint8_t *pa = spec_ipv6[i].addr; |
| pj_uint8_t *pm = spec_ipv6[i].mask; |
| unsigned k; |
| |
| for (k=0; k<16; ++k) { |
| am[k] = (pj_uint8_t)((a[k] & pm[k]) & 0xFF); |
| } |
| |
| if (pj_memcmp(am, pa, 16)==0) { |
| cand_weight[j] += spec_ipv6[i].weight; |
| } |
| } |
| } |
| } else { |
| return PJ_EAFNOTSUP; |
| } |
| |
| /* Enumerate candidates to get the best IP address to choose */ |
| selected_cand = -1; |
| for (i=0; i<cand_cnt; ++i) { |
| TRACE_((THIS_FILE, "Checking candidate IP %s, weight=%d", |
| pj_sockaddr_print(&cand_addr[i], strip, sizeof(strip), 0), |
| cand_weight[i])); |
| |
| if (cand_weight[i] < MIN_WEIGHT) { |
| continue; |
| } |
| |
| if (selected_cand == -1) |
| selected_cand = i; |
| else if (cand_weight[i] > cand_weight[selected_cand]) |
| selected_cand = i; |
| } |
| |
| /* If else fails, returns loopback interface as the last resort */ |
| if (selected_cand == -1) { |
| if (af==PJ_AF_INET) { |
| addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001); |
| } else { |
| pj_in6_addr *s6_addr; |
| |
| s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr); |
| pj_bzero(s6_addr, sizeof(pj_in6_addr)); |
| s6_addr->s6_addr[15] = 1; |
| } |
| TRACE_((THIS_FILE, "Loopback IP %s returned", |
| pj_sockaddr_print(addr, strip, sizeof(strip), 0))); |
| } else { |
| pj_sockaddr_copy_addr(addr, &cand_addr[selected_cand]); |
| TRACE_((THIS_FILE, "Candidate %s selected", |
| pj_sockaddr_print(addr, strip, sizeof(strip), 0))); |
| } |
| |
| return PJ_SUCCESS; |
| } |
| |
| /* Get IP interface for sending to the specified destination */ |
| PJ_DEF(pj_status_t) pj_getipinterface(int af, |
| const pj_str_t *dst, |
| pj_sockaddr *itf_addr, |
| pj_bool_t allow_resolve, |
| pj_sockaddr *p_dst_addr) |
| { |
| pj_sockaddr dst_addr; |
| pj_sock_t fd; |
| int len; |
| pj_uint8_t zero[64]; |
| pj_status_t status; |
| |
| pj_sockaddr_init(af, &dst_addr, NULL, 53); |
| status = pj_inet_pton(af, dst, pj_sockaddr_get_addr(&dst_addr)); |
| if (status != PJ_SUCCESS) { |
| /* "dst" is not an IP address. */ |
| if (allow_resolve) { |
| status = pj_sockaddr_init(af, &dst_addr, dst, 53); |
| } else { |
| pj_str_t cp; |
| |
| if (af == PJ_AF_INET) { |
| cp = pj_str("1.1.1.1"); |
| } else { |
| cp = pj_str("1::1"); |
| } |
| status = pj_sockaddr_init(af, &dst_addr, &cp, 53); |
| } |
| |
| if (status != PJ_SUCCESS) |
| return status; |
| } |
| |
| /* Create UDP socket and connect() to the destination IP */ |
| status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd); |
| if (status != PJ_SUCCESS) { |
| return status; |
| } |
| |
| status = pj_sock_connect(fd, &dst_addr, pj_sockaddr_get_len(&dst_addr)); |
| if (status != PJ_SUCCESS) { |
| pj_sock_close(fd); |
| return status; |
| } |
| |
| len = sizeof(*itf_addr); |
| status = pj_sock_getsockname(fd, itf_addr, &len); |
| if (status != PJ_SUCCESS) { |
| pj_sock_close(fd); |
| return status; |
| } |
| |
| pj_sock_close(fd); |
| |
| /* Check that the address returned is not zero */ |
| pj_bzero(zero, sizeof(zero)); |
| if (pj_memcmp(pj_sockaddr_get_addr(itf_addr), zero, |
| pj_sockaddr_get_addr_len(itf_addr))==0) |
| { |
| return PJ_ENOTFOUND; |
| } |
| |
| if (p_dst_addr) |
| *p_dst_addr = dst_addr; |
| |
| return PJ_SUCCESS; |
| } |
| |
| /* Get the default IP interface */ |
| PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr) |
| { |
| pj_str_t cp; |
| |
| if (af == PJ_AF_INET) { |
| cp = pj_str("1.1.1.1"); |
| } else { |
| cp = pj_str("1::1"); |
| } |
| |
| return pj_getipinterface(af, &cp, addr, PJ_FALSE, NULL); |
| } |
| |
| |
| /* |
| * Bind socket at random port. |
| */ |
| PJ_DEF(pj_status_t) pj_sock_bind_random( pj_sock_t sockfd, |
| const pj_sockaddr_t *addr, |
| pj_uint16_t port_range, |
| pj_uint16_t max_try) |
| { |
| pj_sockaddr bind_addr; |
| int addr_len; |
| pj_uint16_t base_port; |
| pj_status_t status = PJ_SUCCESS; |
| |
| PJ_CHECK_STACK(); |
| |
| PJ_ASSERT_RETURN(addr, PJ_EINVAL); |
| |
| pj_sockaddr_cp(&bind_addr, addr); |
| addr_len = pj_sockaddr_get_len(addr); |
| base_port = pj_sockaddr_get_port(addr); |
| |
| if (base_port == 0 || port_range == 0) { |
| return pj_sock_bind(sockfd, &bind_addr, addr_len); |
| } |
| |
| for (; max_try; --max_try) { |
| pj_uint16_t port; |
| port = (pj_uint16_t)(base_port + pj_rand() % (port_range + 1)); |
| pj_sockaddr_set_port(&bind_addr, port); |
| status = pj_sock_bind(sockfd, &bind_addr, addr_len); |
| if (status == PJ_SUCCESS) |
| break; |
| } |
| |
| return status; |
| } |
| |
| |
| /* |
| * Adjust socket send/receive buffer size. |
| */ |
| PJ_DEF(pj_status_t) pj_sock_setsockopt_sobuf( pj_sock_t sockfd, |
| pj_uint16_t optname, |
| pj_bool_t auto_retry, |
| unsigned *buf_size) |
| { |
| pj_status_t status; |
| int try_size, cur_size, i, step, size_len; |
| enum { MAX_TRY = 20 }; |
| |
| PJ_CHECK_STACK(); |
| |
| PJ_ASSERT_RETURN(sockfd != PJ_INVALID_SOCKET && |
| buf_size && |
| *buf_size > 0 && |
| (optname == pj_SO_RCVBUF() || |
| optname == pj_SO_SNDBUF()), |
| PJ_EINVAL); |
| |
| size_len = sizeof(cur_size); |
| status = pj_sock_getsockopt(sockfd, pj_SOL_SOCKET(), optname, |
| &cur_size, &size_len); |
| if (status != PJ_SUCCESS) |
| return status; |
| |
| try_size = *buf_size; |
| step = (try_size - cur_size) / MAX_TRY; |
| if (step < 4096) |
| step = 4096; |
| |
| for (i = 0; i < (MAX_TRY-1); ++i) { |
| if (try_size <= cur_size) { |
| /* Done, return current size */ |
| *buf_size = cur_size; |
| break; |
| } |
| |
| status = pj_sock_setsockopt(sockfd, pj_SOL_SOCKET(), optname, |
| &try_size, sizeof(try_size)); |
| if (status == PJ_SUCCESS) { |
| status = pj_sock_getsockopt(sockfd, pj_SOL_SOCKET(), optname, |
| &cur_size, &size_len); |
| if (status != PJ_SUCCESS) { |
| /* Ops! No info about current size, just return last try size |
| * and quit. |
| */ |
| *buf_size = try_size; |
| break; |
| } |
| } |
| |
| if (!auto_retry) |
| break; |
| |
| try_size -= step; |
| } |
| |
| return status; |
| } |
| |
| |
| /* Only need to implement these in DLL build */ |
| #if defined(PJ_DLL) |
| |
| PJ_DEF(pj_uint16_t) pj_AF_UNSPEC(void) |
| { |
| return PJ_AF_UNSPEC; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_AF_UNIX(void) |
| { |
| return PJ_AF_UNIX; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_AF_INET(void) |
| { |
| return PJ_AF_INET; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_AF_INET6(void) |
| { |
| return PJ_AF_INET6; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_AF_PACKET(void) |
| { |
| return PJ_AF_PACKET; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_AF_IRDA(void) |
| { |
| return PJ_AF_IRDA; |
| } |
| |
| PJ_DEF(int) pj_SOCK_STREAM(void) |
| { |
| return PJ_SOCK_STREAM; |
| } |
| |
| PJ_DEF(int) pj_SOCK_DGRAM(void) |
| { |
| return PJ_SOCK_DGRAM; |
| } |
| |
| PJ_DEF(int) pj_SOCK_RAW(void) |
| { |
| return PJ_SOCK_RAW; |
| } |
| |
| PJ_DEF(int) pj_SOCK_RDM(void) |
| { |
| return PJ_SOCK_RDM; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SOL_SOCKET(void) |
| { |
| return PJ_SOL_SOCKET; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SOL_IP(void) |
| { |
| return PJ_SOL_IP; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SOL_TCP(void) |
| { |
| return PJ_SOL_TCP; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SOL_UDP(void) |
| { |
| return PJ_SOL_UDP; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SOL_IPV6(void) |
| { |
| return PJ_SOL_IPV6; |
| } |
| |
| PJ_DEF(int) pj_IP_TOS(void) |
| { |
| return PJ_IP_TOS; |
| } |
| |
| PJ_DEF(int) pj_IPTOS_LOWDELAY(void) |
| { |
| return PJ_IPTOS_LOWDELAY; |
| } |
| |
| PJ_DEF(int) pj_IPTOS_THROUGHPUT(void) |
| { |
| return PJ_IPTOS_THROUGHPUT; |
| } |
| |
| PJ_DEF(int) pj_IPTOS_RELIABILITY(void) |
| { |
| return PJ_IPTOS_RELIABILITY; |
| } |
| |
| PJ_DEF(int) pj_IPTOS_MINCOST(void) |
| { |
| return PJ_IPTOS_MINCOST; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SO_TYPE(void) |
| { |
| return PJ_SO_TYPE; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SO_RCVBUF(void) |
| { |
| return PJ_SO_RCVBUF; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SO_SNDBUF(void) |
| { |
| return PJ_SO_SNDBUF; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_TCP_NODELAY(void) |
| { |
| return PJ_TCP_NODELAY; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void) |
| { |
| return PJ_SO_REUSEADDR; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SO_NOSIGPIPE(void) |
| { |
| return PJ_SO_NOSIGPIPE; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_SO_PRIORITY(void) |
| { |
| return PJ_SO_PRIORITY; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_IF(void) |
| { |
| return PJ_IP_MULTICAST_IF; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_TTL(void) |
| { |
| return PJ_IP_MULTICAST_TTL; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_LOOP(void) |
| { |
| return PJ_IP_MULTICAST_LOOP; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_IP_ADD_MEMBERSHIP(void) |
| { |
| return PJ_IP_ADD_MEMBERSHIP; |
| } |
| |
| PJ_DEF(pj_uint16_t) pj_IP_DROP_MEMBERSHIP(void) |
| { |
| return PJ_IP_DROP_MEMBERSHIP; |
| } |
| |
| PJ_DEF(int) pj_MSG_OOB(void) |
| { |
| return PJ_MSG_OOB; |
| } |
| |
| PJ_DEF(int) pj_MSG_PEEK(void) |
| { |
| return PJ_MSG_PEEK; |
| } |
| |
| PJ_DEF(int) pj_MSG_DONTROUTE(void) |
| { |
| return PJ_MSG_DONTROUTE; |
| } |
| |
| #endif /* PJ_DLL */ |
| |