/* 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; }
/* * Accept incoming connections */ PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, pj_sock_t *newsock, pj_sockaddr_t *addr, int *addrlen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL); #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 if (addr) { PJ_SOCKADDR_SET_LEN(addr, *addrlen); } #endif *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); if (*newsock==PJ_INVALID_SOCKET) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 if (addr) { PJ_SOCKADDR_RESET_LEN(addr); } #endif return PJ_SUCCESS; } }
/* * 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 = PJ_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; }
/* * Get socket name. */ PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, pj_sockaddr_t *addr, int *namelen) { PJ_CHECK_STACK(); if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { PJ_SOCKADDR_RESET_LEN(addr); return PJ_SUCCESS; } }
/* * 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); }
/* * Receive data. */ PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, void *buf, pj_ssize_t *len, unsigned flags, pj_sockaddr_t *from, int *fromlen) { PJ_CHECK_STACK(); PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL)); *len = recvfrom(sock, (char*)buf, *len, flags, (struct sockaddr*)from, (socklen_t*)fromlen); if (*len < 0) return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); else { PJ_SOCKADDR_RESET_LEN(from); return PJ_SUCCESS; } }
/* 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 TRACE_((THIS_FILE, "pj_gethostip() pj_getaddrinfo1")); /* 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))); } TRACE_((THIS_FILE, "pj_gethostip() pj_getaddrinfo2")); #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; }
/* Resolve IPv4/IPv6 address */ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, unsigned *count, pj_addrinfo ai[]) { #if defined(PJ_SOCK_HAS_GETADDRINFO) && PJ_SOCK_HAS_GETADDRINFO!=0 char nodecopy[PJ_MAX_HOSTNAME]; struct addrinfo hint, *res, *orig_res; unsigned i; int rc; PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); PJ_ASSERT_RETURN(nodename->ptr && nodename->slen, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if (af == PJ_AF_UNSPEC) { if (pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; } else if (pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; } if (af != PJ_AF_UNSPEC) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } } /* Copy node name to null terminated string. */ if (nodename->slen >= PJ_MAX_HOSTNAME) return PJ_ENAMETOOLONG; pj_memcpy(nodecopy, nodename->ptr, nodename->slen); nodecopy[nodename->slen] = '\0'; /* Call getaddrinfo() */ pj_bzero(&hint, sizeof(hint)); hint.ai_family = af; rc = getaddrinfo(nodecopy, NULL, &hint, &res); if (rc != 0) return PJ_ERESOLVE; orig_res = res; /* Enumerate each item in the result */ for (i=0; i<*count && res; res=res->ai_next) { /* Ignore unwanted address families */ if (af!=PJ_AF_UNSPEC && res->ai_family != af) continue; /* Store canonical name (possibly truncating the name) */ if (res->ai_canonname) { pj_ansi_strncpy(ai[i].ai_canonname, res->ai_canonname, sizeof(ai[i].ai_canonname)); ai[i].ai_canonname[sizeof(ai[i].ai_canonname)-1] = '\0'; } else { pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); } /* Store address */ PJ_ASSERT_ON_FAIL(res->ai_addrlen <= sizeof(pj_sockaddr), continue); pj_memcpy(&ai[i].ai_addr, res->ai_addr, res->ai_addrlen); PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); /* Next slot */ ++i; } *count = i; freeaddrinfo(orig_res); /* Done */ return PJ_SUCCESS; #else /* PJ_SOCK_HAS_GETADDRINFO */ PJ_ASSERT_RETURN(count && *count, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if (af == PJ_AF_UNSPEC) { if (pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; } else if (pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; } if (af != PJ_AF_UNSPEC) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } } if (af == PJ_AF_INET || af == PJ_AF_UNSPEC) { pj_hostent he; unsigned i, max_count; pj_status_t status; /* VC6 complains that "he" is uninitialized */ #ifdef _MSC_VER pj_bzero(&he, sizeof(he)); #endif status = pj_gethostbyname(nodename, &he); if (status != PJ_SUCCESS) return status; max_count = *count; *count = 0; pj_bzero(ai, max_count * sizeof(pj_addrinfo)); for (i=0; he.h_addr_list[i] && *count<max_count; ++i) { pj_ansi_strncpy(ai[*count].ai_canonname, he.h_name, sizeof(ai[*count].ai_canonname)); ai[*count].ai_canonname[sizeof(ai[*count].ai_canonname)-1] = '\0'; ai[*count].ai_addr.ipv4.sin_family = PJ_AF_INET; pj_memcpy(&ai[*count].ai_addr.ipv4.sin_addr, he.h_addr_list[i], he.h_length); PJ_SOCKADDR_RESET_LEN(&ai[*count].ai_addr); (*count)++; } return PJ_SUCCESS; } else { /* IPv6 is not supported */ *count = 0; return PJ_EIPV6NOTSUP; } #endif /* PJ_SOCK_HAS_GETADDRINFO */ }
/* Resolve IPv4/IPv6 address */ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, unsigned *count, pj_addrinfo ai[]) { #if defined(PJ_SOCK_HAS_GETADDRINFO) && PJ_SOCK_HAS_GETADDRINFO!=0 char nodecopy[PJ_MAX_HOSTNAME]; pj_bool_t has_addr = PJ_FALSE; unsigned i; #if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 CFStringRef hostname; CFHostRef hostRef; pj_status_t status = PJ_SUCCESS; #else int rc; struct addrinfo hint, *res, *orig_res; #endif PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); PJ_ASSERT_RETURN(nodename->ptr && nodename->slen, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if ((af==PJ_AF_INET || af==PJ_AF_UNSPEC) && pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; has_addr = PJ_TRUE; } else if ((af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) && pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; has_addr = PJ_TRUE; } if (has_addr) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } /* Copy node name to null terminated string. */ if (nodename->slen >= PJ_MAX_HOSTNAME) return PJ_ENAMETOOLONG; pj_memcpy(nodecopy, nodename->ptr, nodename->slen); nodecopy[nodename->slen] = '\0'; #if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0 hostname = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, nodecopy, kCFStringEncodingASCII, kCFAllocatorNull); hostRef = CFHostCreateWithName(kCFAllocatorDefault, hostname); if (CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil)) { CFArrayRef addrRef = CFHostGetAddressing(hostRef, nil); i = 0; if (addrRef != nil) { CFIndex idx, naddr; naddr = CFArrayGetCount(addrRef); for (idx = 0; idx < naddr && i < *count; idx++) { struct sockaddr *addr; addr = (struct sockaddr *) CFDataGetBytePtr(CFArrayGetValueAtIndex(addrRef, idx)); /* This should not happen. */ pj_assert(addr); /* Ignore unwanted address families */ if (af!=PJ_AF_UNSPEC && addr->sa_family != af) continue; /* Store canonical name */ pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); /* Store address */ PJ_ASSERT_ON_FAIL(sizeof(*addr) <= sizeof(pj_sockaddr), continue); pj_memcpy(&ai[i].ai_addr, addr, sizeof(*addr)); PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); i++; } } *count = i; } else {