/* 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; }
// PJLIB API: resolve hostname PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he) { static pj_addrinfo ai; static char *aliases[2]; static char *addrlist[2]; unsigned count = 1; pj_status_t status; status = pj_getaddrinfo(PJ_AF_INET, name, &count, &ai); if (status != PJ_SUCCESS) return status; aliases[0] = ai.ai_canonname; aliases[1] = NULL; addrlist[0] = (char*) &ai.ai_addr.ipv4.sin_addr; addrlist[1] = NULL; pj_bzero(he, sizeof(*he)); he->h_name = aliases[0]; he->h_aliases = aliases; he->h_addrtype = PJ_AF_INET; he->h_length = 4; he->h_addr_list = addrlist; return PJ_SUCCESS; }
/* Start socket. */ PJ_DEF(pj_status_t) pj_stun_sock_start( pj_stun_sock *stun_sock, const pj_str_t *domain, pj_uint16_t default_port, pj_dns_resolver *resolver) { pj_status_t status; PJ_ASSERT_RETURN(stun_sock && domain && default_port, PJ_EINVAL); /* Check whether the domain contains IP address */ stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)stun_sock->af; status = pj_inet_pton(stun_sock->af, domain, pj_sockaddr_get_addr(&stun_sock->srv_addr)); if (status != PJ_SUCCESS) { stun_sock->srv_addr.addr.sa_family = (pj_uint16_t)0; } /* If resolver is set, try to resolve with DNS SRV first. It * will fallback to DNS A/AAAA when no SRV record is found. */ if (status != PJ_SUCCESS && resolver) { const pj_str_t res_name = pj_str("_stun._udp."); unsigned opt; pj_assert(stun_sock->q == NULL); opt = PJ_DNS_SRV_FALLBACK_A; if (stun_sock->af == pj_AF_INET6()) { opt |= (PJ_DNS_SRV_RESOLVE_AAAA | PJ_DNS_SRV_FALLBACK_AAAA); } status = pj_dns_srv_resolve(domain, &res_name, default_port, stun_sock->pool, resolver, opt, stun_sock, &dns_srv_resolver_cb, &stun_sock->q); /* Processing will resume when the DNS SRV callback is called */ return status; } else { if (status != PJ_SUCCESS) { pj_addrinfo ai; unsigned cnt = 1; status = pj_getaddrinfo(stun_sock->af, domain, &cnt, &ai); if (status != PJ_SUCCESS) return status; pj_sockaddr_cp(&stun_sock->srv_addr, &ai.ai_addr); } pj_sockaddr_set_port(&stun_sock->srv_addr, (pj_uint16_t)default_port); /* Start sending Binding request */ return get_mapped_addr(stun_sock); } }
pj_status_t fill_transport_details(int port, pj_sockaddr *addr, pj_str_t& host, pjsip_host_port *published_name) { pj_status_t status; unsigned count = 1; pj_addrinfo addr_info[count]; int af = pj_AF_UNSPEC(); // Use pj_getaddrinfo() to convert the localhost string into an IPv4 or IPv6 address in // a pj_sockaddr structure. The localhost string could be an IP address in string format // or a hostname that needs to be resolved. The localhost string should only contain a // single address or hostname. // Bono/Sprout needs to bind to the local host, but use the host passed into this // function in the route header (which can be the local or public host) status = pj_getaddrinfo(af, &stack_data.local_host, &count, addr_info); if (status != PJ_SUCCESS) { LOG_ERROR("Failed to decode IP address %.*s (%s)", stack_data.local_host.slen, stack_data.local_host.ptr, PJUtils::pj_status_to_string(status).c_str()); return status; } pj_memcpy(addr, &addr_info[0].ai_addr, sizeof(pj_sockaddr)); // Set up the port in the appropriate part of the structure. if (addr->addr.sa_family == PJ_AF_INET) { addr->ipv4.sin_port = pj_htons((pj_uint16_t)port); } else if (addr->addr.sa_family == PJ_AF_INET6) { addr->ipv6.sin6_port = pj_htons((pj_uint16_t)port); } else { status = PJ_EAFNOTSUP; } published_name->host = host; published_name->port = port; return status; }
std::vector<IpAddr> ip_utils::getAddrList(const std::string &name, pj_uint16_t family) { std::vector<IpAddr> ipList; if (name.empty()) return ipList; if (IpAddr::isValid(name, family)) { ipList.push_back(name); return ipList; } static const unsigned MAX_ADDR_NUM = 128; pj_addrinfo res[MAX_ADDR_NUM]; unsigned addr_num = MAX_ADDR_NUM; pj_str_t pjname; pj_cstr(&pjname, name.c_str()); auto status = pj_getaddrinfo(family, &pjname, &addr_num, res); if (status != PJ_SUCCESS) { ERROR("Error resolving %s :", name.c_str()); sip_utils::sip_strerror(status); return ipList; } for (unsigned i=0; i<addr_num; i++) { bool found = false; for (const auto& ip : ipList) if (!pj_sockaddr_cmp(&ip, &res[i].ai_addr)) { found = true; break; } if (!found) ipList.push_back(res[i].ai_addr); } return ipList; }
/* 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; }
/* * This is the main function for performing server resolution. */ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { pjsip_server_addresses svr_addr; pj_status_t status = PJ_SUCCESS; int ip_addr_ver; struct query *query; pjsip_transport_type_e type = target->type; /* Is it IP address or hostname? And if it's an IP, which version? */ ip_addr_ver = get_ip_addr_ver(&target->addr.host); /* Set the transport type if not explicitly specified. * RFC 3263 section 4.1 specify rules to set up this. */ if (type == PJSIP_TRANSPORT_UNSPECIFIED) { if (ip_addr_ver || (target->addr.port != 0)) { #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } else { /* No type or explicit port is specified, and the address is * not IP address. * In this case, full NAPTR resolution must be performed. * But we don't support it (yet). */ #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } /* Add IPv6 flag for IPv6 address */ if (ip_addr_ver == 6) type = (pjsip_transport_type_e)((int)type + PJSIP_TRANSPORT_IPV6); } /* If target is an IP address, or if resolver is not configured, * we can just finish the resolution now using pj_gethostbyname() */ if (ip_addr_ver || resolver->res == NULL) { char addr_str[PJ_INET6_ADDRSTRLEN+10]; pj_uint16_t srv_port; if (ip_addr_ver != 0) { /* Target is an IP address, no need to resolve */ if (ip_addr_ver == 4) { pj_sockaddr_init(pj_AF_INET(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_aton(&target->addr.host, &svr_addr.entry[0].addr.ipv4.sin_addr); } else { pj_sockaddr_init(pj_AF_INET6(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_pton(pj_AF_INET6(), &target->addr.host, &svr_addr.entry[0].addr.ipv6.sin6_addr); } } else { pj_addrinfo ai; unsigned count; int af; PJ_LOG(5,(THIS_FILE, "DNS resolver not available, target '%.*s:%d' type=%s " "will be resolved with getaddrinfo()", target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type))); if (type & PJSIP_TRANSPORT_IPV6) { af = pj_AF_INET6(); } else { af = pj_AF_INET(); } /* Resolve */ count = 1; status = pj_getaddrinfo(af, &target->addr.host, &count, &ai); if (status != PJ_SUCCESS) goto on_error; svr_addr.entry[0].addr.addr.sa_family = (pj_uint16_t)af; pj_memcpy(&svr_addr.entry[0].addr, &ai.ai_addr, sizeof(pj_sockaddr)); } /* Set the port number */ if (target->addr.port == 0) { srv_port = (pj_uint16_t) pjsip_transport_get_default_port_for_type(type); } else { srv_port = (pj_uint16_t)target->addr.port; } pj_sockaddr_set_port(&svr_addr.entry[0].addr, srv_port); /* Call the callback. */ PJ_LOG(5,(THIS_FILE, "Target '%.*s:%d' type=%s resolved to " "'%s' type=%s (%s)", (int)target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type), pj_sockaddr_print(&svr_addr.entry[0].addr, addr_str, sizeof(addr_str), 3), pjsip_transport_get_type_name(type), pjsip_transport_get_type_desc(type))); svr_addr.count = 1; svr_addr.entry[0].priority = 0; svr_addr.entry[0].weight = 0; svr_addr.entry[0].type = type; svr_addr.entry[0].addr_len = pj_sockaddr_get_len(&svr_addr.entry[0].addr); (*cb)(status, token, &svr_addr); /* Done. */ return; } /* Target is not an IP address so we need to resolve it. */ #if PJSIP_HAS_RESOLVER /* Build the query state */ query = PJ_POOL_ZALLOC_T(pool, struct query); query->objname = THIS_FILE; query->token = token; query->cb = cb; query->req.target = *target; pj_strdup(pool, &query->req.target.addr.host, &target->addr.host); /* If port is not specified, start with SRV resolution * (should be with NAPTR, but we'll do that later) */ PJ_TODO(SUPPORT_DNS_NAPTR); /* Build dummy NAPTR entry */ query->naptr_cnt = 1; pj_bzero(&query->naptr[0], sizeof(query->naptr[0])); query->naptr[0].order = 0; query->naptr[0].pref = 0; query->naptr[0].type = type; pj_strdup(pool, &query->naptr[0].name, &target->addr.host); /* Start DNS SRV or A resolution, depending on whether port is specified */ if (target->addr.port == 0) { query->query_type = PJ_DNS_TYPE_SRV; query->req.def_port = 5060; if (type == PJSIP_TRANSPORT_TLS) { query->naptr[0].res_type = pj_str("_sips._tcp."); query->req.def_port = 5061; } else if (type == PJSIP_TRANSPORT_TCP) query->naptr[0].res_type = pj_str("_sip._tcp."); else if (type == PJSIP_TRANSPORT_UDP) query->naptr[0].res_type = pj_str("_sip._udp."); else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); } } else { /* Otherwise if port is specified, start with A (or AAAA) host * resolution */ query->query_type = PJ_DNS_TYPE_A; query->naptr[0].res_type.slen = 0; query->req.def_port = target->addr.port; } /* Start the asynchronous query */ PJ_LOG(5, (query->objname, "Starting async DNS %s query: target=%.*s%.*s, transport=%s, " "port=%d", pj_dns_get_type_name(query->query_type), (int)query->naptr[0].res_type.slen, query->naptr[0].res_type.ptr, (int)query->naptr[0].name.slen, query->naptr[0].name.ptr, pjsip_transport_get_type_name(target->type), target->addr.port)); if (query->query_type == PJ_DNS_TYPE_SRV) { unsigned option = PJ_TRUE; if (type & PJSIP_TRANSPORT_IPV6) { option |= PJ_DNS_SRV_FALLBACK_GETADDRINFO_IPV6; } else { option |= PJ_DNS_SRV_FALLBACK_GETADDRINFO_IPV4; } status = pj_dns_srv_resolve(&query->naptr[0].name, &query->naptr[0].res_type, query->req.def_port, pool, resolver->res, option, query, &srv_resolver_cb, NULL); } else if (query->query_type == PJ_DNS_TYPE_A) { status = pj_dns_resolver_start_query(resolver->res, &query->naptr[0].name, PJ_DNS_TYPE_A, 0, &dns_a_callback, query, &query->object); } else { pj_assert(!"Unexpected"); status = PJ_EBUG; } if (status != PJ_SUCCESS) goto on_error; return; #else /* PJSIP_HAS_RESOLVER */ PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(query); PJ_UNUSED_ARG(srv_name); #endif /* PJSIP_HAS_RESOLVER */ on_error: if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)", (int)target->addr.host.slen, target->addr.host.ptr, status, pj_strerror(status,errmsg,sizeof(errmsg)).ptr)); (*cb)(status, token, NULL); return; } }
static int purity_test(void) { PJ_LOG(3,("test", "...purity_test()")); #if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0 /* Check on "sin_len" member of sockaddr */ { const pj_str_t str_ip = {"1.1.1.1", 7}; pj_sockaddr addr[16]; pj_addrinfo ai[16]; unsigned cnt; pj_status_t rc; /* pj_enum_ip_interface() */ cnt = PJ_ARRAY_SIZE(addr); rc = pj_enum_ip_interface(pj_AF_UNSPEC(), &cnt, addr); if (rc == PJ_SUCCESS) { while (cnt--) CHECK_SA_ZERO_LEN(&addr[cnt], -10); } /* pj_gethostip() on IPv4 */ rc = pj_gethostip(pj_AF_INET(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -20); /* pj_gethostip() on IPv6 */ rc = pj_gethostip(pj_AF_INET6(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -30); /* pj_getdefaultipinterface() on IPv4 */ rc = pj_getdefaultipinterface(pj_AF_INET(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -40); /* pj_getdefaultipinterface() on IPv6 */ rc = pj_getdefaultipinterface(pj_AF_INET6(), &addr[0]); if (rc == PJ_SUCCESS) CHECK_SA_ZERO_LEN(&addr[0], -50); /* pj_getaddrinfo() on a host name */ cnt = PJ_ARRAY_SIZE(ai); rc = pj_getaddrinfo(pj_AF_UNSPEC(), pj_gethostname(), &cnt, ai); if (rc == PJ_SUCCESS) { while (cnt--) CHECK_SA_ZERO_LEN(&ai[cnt].ai_addr, -60); } /* pj_getaddrinfo() on an IP address */ cnt = PJ_ARRAY_SIZE(ai); rc = pj_getaddrinfo(pj_AF_UNSPEC(), &str_ip, &cnt, ai); if (rc == PJ_SUCCESS) { pj_assert(cnt == 1); CHECK_SA_ZERO_LEN(&ai[0].ai_addr, -70); } } #endif return 0; }
/* * This is the main function for performing server resolution. */ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { pjsip_server_addresses svr_addr; pj_status_t status = PJ_SUCCESS; int ip_addr_ver; struct query *query; pjsip_transport_type_e type = target->type; int af = pj_AF_UNSPEC(); /* If an external implementation has been provided use it instead */ if (resolver->ext_res) { (*resolver->ext_res->resolve)(resolver, pool, target, token, cb); return; } /* Is it IP address or hostname? And if it's an IP, which version? */ ip_addr_ver = get_ip_addr_ver(&target->addr.host); /* Initialize address family type. Unfortunately, target type doesn't * really tell the address family type, except when IPv6 flag is * explicitly set. */ #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6==1 if ((ip_addr_ver == 6) || (type & PJSIP_TRANSPORT_IPV6)) af = pj_AF_INET6(); else if (ip_addr_ver == 4) af = pj_AF_INET(); #else /* IPv6 is disabled, will resolving IPv6 address be useful? */ af = pj_AF_INET(); #endif /* Set the transport type if not explicitly specified. * RFC 3263 section 4.1 specify rules to set up this. */ if (type == PJSIP_TRANSPORT_UNSPECIFIED) { if (ip_addr_ver || (target->addr.port != 0)) { #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } else { /* No type or explicit port is specified, and the address is * not IP address. * In this case, full NAPTR resolution must be performed. * But we don't support it (yet). */ #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } } /* If target is an IP address, or if resolver is not configured, * we can just finish the resolution now using pj_gethostbyname() */ if (ip_addr_ver || resolver->res == NULL) { char addr_str[PJ_INET6_ADDRSTRLEN+10]; pj_uint16_t srv_port; if (ip_addr_ver != 0) { /* Target is an IP address, no need to resolve */ if (ip_addr_ver == 4) { pj_sockaddr_init(pj_AF_INET(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_pton(pj_AF_INET(), &target->addr.host, &svr_addr.entry[0].addr.ipv4.sin_addr); } else { pj_sockaddr_init(pj_AF_INET6(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_pton(pj_AF_INET6(), &target->addr.host, &svr_addr.entry[0].addr.ipv6.sin6_addr); } } else { pj_addrinfo ai; unsigned count; PJ_LOG(5,(THIS_FILE, "DNS resolver not available, target '%.*s:%d' type=%s " "will be resolved with getaddrinfo()", target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type))); /* Resolve */ count = 1; status = pj_getaddrinfo(af, &target->addr.host, &count, &ai); if (status != PJ_SUCCESS) { /* "Normalize" error to PJ_ERESOLVE. This is a special error * because it will be translated to SIP status 502 by * sip_transaction.c */ status = PJ_ERESOLVE; goto on_error; } pj_sockaddr_cp(&svr_addr.entry[0].addr, &ai.ai_addr); if (af == pj_AF_UNSPEC()) af = ai.ai_addr.addr.sa_family; } /* After address resolution, update IPv6 bitflag in transport type. */ if (af == pj_AF_INET6()) type |= PJSIP_TRANSPORT_IPV6; /* Set the port number */ if (target->addr.port == 0) { srv_port = (pj_uint16_t) pjsip_transport_get_default_port_for_type(type); } else { srv_port = (pj_uint16_t)target->addr.port; } pj_sockaddr_set_port(&svr_addr.entry[0].addr, srv_port); /* Call the callback. */ PJ_LOG(5,(THIS_FILE, "Target '%.*s:%d' type=%s resolved to " "'%s' type=%s (%s)", (int)target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type), pj_sockaddr_print(&svr_addr.entry[0].addr, addr_str, sizeof(addr_str), 3), pjsip_transport_get_type_name(type), pjsip_transport_get_type_desc(type))); svr_addr.count = 1; svr_addr.entry[0].priority = 0; svr_addr.entry[0].weight = 0; svr_addr.entry[0].type = type; svr_addr.entry[0].addr_len = pj_sockaddr_get_len(&svr_addr.entry[0].addr); (*cb)(status, token, &svr_addr); /* Done. */ return; } /* Target is not an IP address so we need to resolve it. */ #if PJSIP_HAS_RESOLVER /* Build the query state */ query = PJ_POOL_ZALLOC_T(pool, struct query); query->objname = THIS_FILE; query->token = token; query->cb = cb; query->req.target = *target; pj_strdup(pool, &query->req.target.addr.host, &target->addr.host); /* If port is not specified, start with SRV resolution * (should be with NAPTR, but we'll do that later) */ PJ_TODO(SUPPORT_DNS_NAPTR); /* Build dummy NAPTR entry */ query->naptr_cnt = 1; pj_bzero(&query->naptr[0], sizeof(query->naptr[0])); query->naptr[0].order = 0; query->naptr[0].pref = 0; query->naptr[0].type = type; pj_strdup(pool, &query->naptr[0].name, &target->addr.host); /* Start DNS SRV or A resolution, depending on whether port is specified */ if (target->addr.port == 0) { query->query_type = PJ_DNS_TYPE_SRV; query->req.def_port = 5060; if (type == PJSIP_TRANSPORT_TLS || type == PJSIP_TRANSPORT_TLS6) { query->naptr[0].res_type = pj_str("_sips._tcp."); query->req.def_port = 5061; } else if (type == PJSIP_TRANSPORT_TCP || type == PJSIP_TRANSPORT_TCP6) query->naptr[0].res_type = pj_str("_sip._tcp."); else if (type == PJSIP_TRANSPORT_UDP || type == PJSIP_TRANSPORT_UDP6) query->naptr[0].res_type = pj_str("_sip._udp."); else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); } } else { /* Otherwise if port is specified, start with A (or AAAA) host * resolution */ query->query_type = PJ_DNS_TYPE_A; query->naptr[0].res_type.slen = 0; query->req.def_port = target->addr.port; } /* Start the asynchronous query */ PJ_LOG(5, (query->objname, "Starting async DNS %s query: target=%.*s%.*s, transport=%s, " "port=%d", pj_dns_get_type_name(query->query_type), (int)query->naptr[0].res_type.slen, query->naptr[0].res_type.ptr, (int)query->naptr[0].name.slen, query->naptr[0].name.ptr, pjsip_transport_get_type_name(target->type), target->addr.port)); if (query->query_type == PJ_DNS_TYPE_SRV) { int opt = 0; if (af == pj_AF_UNSPEC()) opt = PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA | PJ_DNS_SRV_RESOLVE_AAAA; else if (af == pj_AF_INET6()) opt = PJ_DNS_SRV_FALLBACK_AAAA | PJ_DNS_SRV_RESOLVE_AAAA_ONLY; else /* af == pj_AF_INET() */ opt = PJ_DNS_SRV_FALLBACK_A; status = pj_dns_srv_resolve(&query->naptr[0].name, &query->naptr[0].res_type, query->req.def_port, pool, resolver->res, opt, query, &srv_resolver_cb, NULL); } else if (query->query_type == PJ_DNS_TYPE_A) { /* Resolve DNS A record if address family is not fixed to IPv6 */ if (af != pj_AF_INET6()) { /* If there will be DNS AAAA query too, let's setup a dummy one * here, otherwise app callback may be called immediately (before * DNS AAAA query is sent) when DNS A record is available in the * cache. */ if (af == pj_AF_UNSPEC()) query->object6 = (pj_dns_async_query*)0x1; status = pj_dns_resolver_start_query(resolver->res, &query->naptr[0].name, PJ_DNS_TYPE_A, 0, &dns_a_callback, query, &query->object); } /* Resolve DNS AAAA record if address family is not fixed to IPv4 */ if (af != pj_AF_INET() && status == PJ_SUCCESS) { status = pj_dns_resolver_start_query(resolver->res, &query->naptr[0].name, PJ_DNS_TYPE_AAAA, 0, &dns_aaaa_callback, query, &query->object6); } } else { pj_assert(!"Unexpected"); status = PJ_EBUG; } if (status != PJ_SUCCESS) goto on_error; return; #else /* PJSIP_HAS_RESOLVER */ PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(query); PJ_UNUSED_ARG(srv_name); #endif /* PJSIP_HAS_RESOLVER */ on_error: if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)", (int)target->addr.host.slen, target->addr.host.ptr, status, pj_strerror(status,errmsg,sizeof(errmsg)).ptr)); (*cb)(status, token, NULL); return; } }
/** * Set the server or domain name of the server. */ PJ_DEF(pj_status_t) pj_tcp_session_set_server( pj_tcp_session *sess, const pj_str_t *domain, int default_port, pj_dns_resolver *resolver) { pj_sockaddr tmp_addr; pj_bool_t is_ip_addr; pj_status_t status; PJ_ASSERT_RETURN(sess && domain, PJ_EINVAL); PJ_ASSERT_RETURN(sess->state == PJ_TCP_STATE_NULL, PJ_EINVALIDOP); pj_lock_acquire(sess->lock); /* See if "domain" contains just IP address */ tmp_addr.addr.sa_family = sess->af; status = pj_inet_pton(sess->af, domain, pj_sockaddr_get_addr(&tmp_addr)); is_ip_addr = (status == PJ_SUCCESS); if (!is_ip_addr && resolver) { /* Resolve with DNS SRV resolution, and fallback to DNS A resolution * if default_port is specified. */ unsigned opt = 0; pj_str_t res_name; res_name = pj_str("_tcps._tcp."); /* Fallback to DNS A only if default port is specified */ if (default_port>0 && default_port<65536) { opt = PJ_DNS_SRV_FALLBACK_A; sess->default_port = (pj_uint16_t)default_port; } PJ_LOG(5,(sess->obj_name, "Resolving %.*s%.*s with DNS SRV", (int)res_name.slen, res_name.ptr, (int)domain->slen, domain->ptr)); pj_tcp_session_set_state(sess, PJ_TCP_STATE_RESOLVING); /* User may have destroyed us in the callback */ if (sess->state != PJ_TCP_STATE_RESOLVING) { status = PJ_ECANCELLED; goto on_return; } status = pj_dns_srv_resolve(domain, &res_name, default_port, sess->pool, resolver, opt, sess, &dns_srv_resolver_cb, &sess->dns_async); if (status != PJ_SUCCESS) { pj_tcp_session_set_state(sess, PJ_TCP_STATE_NULL); goto on_return; } } else { /* Resolver is not specified, resolve with standard gethostbyname(). * The default_port MUST be specified in this case. */ pj_addrinfo *ai; unsigned i, cnt; /* Default port must be specified */ PJ_ASSERT_RETURN(default_port>0 && default_port<65536, PJ_EINVAL); sess->default_port = (pj_uint16_t)default_port; cnt = PJ_TCP_MAX_DNS_SRV_CNT; ai = (pj_addrinfo*) pj_pool_calloc(sess->pool, cnt, sizeof(pj_addrinfo)); PJ_LOG(5,(sess->obj_name, "Resolving %.*s with DNS A", (int)domain->slen, domain->ptr)); pj_tcp_session_set_state(sess, PJ_TCP_STATE_RESOLVING); /* User may have destroyed us in the callback */ if (sess->state != PJ_TCP_STATE_RESOLVING) { status = PJ_ECANCELLED; goto on_return; } status = pj_getaddrinfo(sess->af, domain, &cnt, ai); if (status != PJ_SUCCESS) goto on_return; sess->srv_addr_cnt = (pj_uint16_t)cnt; sess->srv_addr_list = (pj_sockaddr*) pj_pool_calloc(sess->pool, cnt, sizeof(pj_sockaddr)); for (i=0; i<cnt; ++i) { pj_sockaddr *addr = &sess->srv_addr_list[i]; pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr)); addr->addr.sa_family = sess->af; addr->ipv4.sin_port = pj_htons(sess->default_port); } sess->srv_addr = &sess->srv_addr_list[0]; pj_tcp_session_set_state(sess, PJ_TCP_STATE_RESOLVED); } on_return: pj_lock_release(sess->lock); return status; }