/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * 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 #include #include #include #include #include #include #include #include #include #if 0 /* Enable some tracing */ #include #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 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 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 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; iaddr.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) { 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 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 */