/**@internal * * Detect NAT. * * Based on "received" and possible "rport" parameters in the top-most Via, * check and update our NAT status. * * @retval ob_nat_changed (2) change in public NAT binding detected * @retval ob_nat_detected (1) NAT binding detected * @retval ob_no_nat (0) no NAT binding detected * @retval -1 an error occurred */ static int outbound_nat_detect(outbound_t *ob, sip_t const *request, sip_t const *response) { sip_via_t const *v; int one = 1; char const *received, *rport; char *nat_detected, *nat_port; char *new_detected, *new_port; assert(request && request->sip_request); assert(response && response->sip_status); if (!ob || !response || !response->sip_via || !request->sip_via) return -1; v = response->sip_via; received = v->v_received; if (!received || !strcmp(received, request->sip_via->v_host)) return 0; if (!host_is_ip_address(received)) { if (received[0]) SU_DEBUG_3(("outbound(%p): Via with invalid received=%s\n", (void *)ob->ob_owner, received)); return 0; } rport = sip_via_port(v, &one); assert(rport); nat_detected = ob->ob_nat_detected; nat_port = ob->ob_nat_port; if (nat_detected && host_cmp(received, nat_detected) == 0) { if (nat_port && su_casematch(rport, nat_port)) return 1; if (!v->v_rport || !v->v_rport[0]) return 1; } if (!nat_detected) { SU_DEBUG_5(("outbound(%p): detected NAT: %s != %s\n", (void *)ob->ob_owner, v->v_host, received)); if (ob->ob_oo && ob->ob_oo->oo_status) ob->ob_oo->oo_status(ob->ob_owner, ob, 101, "NAT detected", TAG_END()); } else { SU_DEBUG_5(("outbound(%p): NAT binding changed: " "[%s]:%s != [%s]:%s\n", (void *)ob->ob_owner, nat_detected, nat_port, received, rport)); if (ob->ob_oo && ob->ob_oo->oo_status) ob->ob_oo->oo_status(ob->ob_owner, ob, 102, "NAT binding changed", TAG_END()); } /* Save our nat binding */ new_detected = su_strdup(ob->ob_home, received); new_port = su_strdup(ob->ob_home, rport); if (!new_detected || !new_port) { su_free(ob->ob_home, new_detected); su_free(ob->ob_home, new_port); return -1; } ob->ob_nat_detected = new_detected; ob->ob_nat_port = new_port; su_free(ob->ob_home, nat_detected); su_free(ob->ob_home, nat_port); return 2; }
/** Convert a @Via header to @Contact URL string. * * The @Contact URI will contain the port number and transport parameters if * needed. If transport protocol name starts with "TLS", "SIPS:" URI schema * is used. * * The contact URI string returned will always have angle brackets ("<" and * ">") around it. * * @param home memory home * @param v @Via header field structure * (with <sent-by> parameter containing host and port) * @param user username for @Contact URI (may be NULL) * @param transport transport name for @Contact URI (may be NULL) * * @retval string containing Contact URI with angle brackets * @retval NULL upon an error */ char * sip_contact_string_from_via(su_home_t *home, sip_via_t const *v, char const *user, char const *transport) { const char *host, *port, *maddr, *comp; char const *scheme = "sip:"; int one = 1; char _transport[16]; if (!v) return NULL; host = v->v_host; if (v->v_received) host = v->v_received; port = sip_via_port(v, &one); maddr = v->v_maddr; comp = v->v_comp; if (host == NULL) return NULL; if (sip_transport_has_tls(v->v_protocol) || sip_transport_has_tls(transport)) { scheme = "sips:"; if (port && strcmp(port, SIPS_DEFAULT_SERV) == 0) port = NULL; if (port || host_is_ip_address(host)) transport = NULL; } else if (port && strcmp(port, SIP_DEFAULT_SERV) == 0 && (host_is_ip_address(host) || host_has_domain_invalid(host))) { port = NULL; } if (su_casenmatch(transport, "SIP/2.0/", 8)) transport += 8; /* Make transport parameter lowercase */ if (transport && strlen(transport) < (sizeof _transport)) { char *s = strcpy(_transport, transport); short c; for (s = _transport; (c = *s) && c != ';'; s++) if (isupper(c)) *s = tolower(c); transport = _transport; } return su_strcat_all(home, "<", scheme, user ? user : "", user ? "@" : "", host, SIP_STRLOG(":", port), SIP_STRLOG(";transport=", transport), SIP_STRLOG(";maddr=", maddr), SIP_STRLOG(";comp=", comp), ">", NULL); }