Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.562 diff -u -r1.562 chan_sip.c --- channels/chan_sip.c 13 Nov 2004 16:22:44 -0000 1.562 +++ channels/chan_sip.c 14 Nov 2004 11:19:06 -0000 @@ -129,7 +129,7 @@ static char notifymime[AST_MAX_EXTENSION] = "application/simple-message-summary"; -static int srvlookup = 0; /* SRV Lookup on or off. Default is off, RFC behavior is on */ +static int global_srvlookup = 0; /* SRV Lookup on or off. Default is off, RFC behavior is on */ static int pedanticsipchecking = 0; /* Extra checking ? Default off */ @@ -145,6 +145,11 @@ static int global_progressinband = 0; +/* Outbound proxy support, configurable in sip.conf */ +static char global_outboundproxy[AST_MAX_EXTENSION] = ""; +static struct sockaddr_in outboundproxyip; + + #ifdef OSP_SUPPORT static int global_ospauth = 0; #endif @@ -519,6 +524,13 @@ int recheck; } regl; +/*--- srvresult: Structure for returning DNS srv results ---*/ +struct srvresult { + struct hostent *hp; + int portno; +}; + + #define REINVITE_INVITE 1 #define REINVITE_UPDATE 2 @@ -554,6 +566,37 @@ static int sip_do_reload(void); +/*--- find_host: Find SIP host with DNS SRV and gethostbyname ---*/ +/* OEJ: If we find two SRV records, only the first one is used. Bad, bad, bad */ + +static struct srvresult *find_host(char *domain, int port) +{ + char service[256]; + char host[256]; + char *hostn = domain; + struct ast_hostent ahp; + int tportno=port; + int ret; + struct srvresult *res; + + res = malloc(sizeof(struct srvresult)); + res->portno = tportno; + + /* Only do SRV lookups if port is standard SIP port */ + if (global_srvlookup && port == 5060) { /* SRV Lookups are optional */ + snprintf(service, sizeof(service), "_sip._udp.%s", domain); + ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); + + if (ret > 0) { + hostn = host; + res->portno = tportno; /* What happens here, new port number? Should be returned.. */ + + } + } + res->hp=ast_gethostbyname(hostn, &ahp); + return res; +} + /*--- sip_debug_test_addr: See if we pass debug IP filter */ static inline int sip_debug_test_addr(struct sockaddr_in *addr) { @@ -581,12 +624,69 @@ { int res; char iabuf[INET_ADDRSTRLEN]; - if (p->nat & SIP_NAT_ROUTE) - res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->recv, sizeof(struct sockaddr_in)); - else - res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_in)); + char iabuf2[INET_ADDRSTRLEN]; + struct sockaddr_in *target; /* Where to send this packet */ + int targetset = 0; /* Is target set? */ + + /* First we need to find out the address to send to, one of + * The address we got from the other side, from registration + or host= config + * The outbound proxy + * The address we got packets from (NAT=yes) + */ + /* Default: Send to the address we got in the message or in registration */ + if (ast_strlen_zero(global_outboundproxy) && (p->nat & SIP_NAT_NEVER)) { + /* No nat support, no outbound proxy, default to old behaviour */ + res = sendto(sipsock, data, len, 0, (struct sockaddr *) &p->sa, sizeof(struct sockaddr_in)); + } else { + + target = (struct sockaddr_in *) &p->sa; + + + /* If we have outbound proxy configured - send everything to it */ + if (!ast_strlen_zero(global_outboundproxy)) { + if (!localaddr) { /* No local address (NAT) config */ + target = (struct sockaddr_in *) &outboundproxyip; + targetset = 1; + if (option_verbose > 3 && sipdebug) + ast_verbose(VERBOSE_PREFIX_4 "sip_xmit: %p (len %d) to %s sent via outbound proxy %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), ast_inet_ntoa(iabuf2, sizeof(iabuf2), outboundproxyip.sin_addr)); + } else { + /* If the host is within the local network boundary, + don't use outbound proxy + (fallback to ordinary NAT/no nat behaviour */ + + if(ast_apply_ha(localaddr, target) == AST_SENSE_ALLOW) { + /* We are on the outside of localnet= networks, + send to outbound proxy */ + + target = (struct sockaddr_in *) &outboundproxyip; + targetset = 1; + if (sipdebug && option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "sip_xmit: %p (len %d) to %s sent via outbound proxy\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr)); + } else { + if (sipdebug && option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 "sip_xmit: Outbound proxy overrun by localnet setting. Not using outbound proxy.\n"); + } + } + } + + /* If nat=yes, send to the ip & port we received the data from (Symmetric SIP signalling) */ + if (!targetset && p->nat & SIP_NAT_ROUTE) { + if (!p->recv.sin_addr.s_addr) { + ast_log(LOG_WARNING, "sip_xmit NAT - no address to send to, skipping\n"); + return -1; /* No active address to send to, skip it */ + } + target = (struct sockaddr_in *) &p->recv; + } + if (sipdebug && option_verbose > 3) + ast_verbose(VERBOSE_PREFIX_4 ">>> Sending SIP message to %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), target->sin_addr)); + + /* Ok, we are ready to send the packet */ + res = sendto(sipsock, data, len, 0, (struct sockaddr *) target, sizeof(struct sockaddr_in)); + } + if (res != len) { - ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), res, strerror(errno)); + ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), target->sin_addr), res, strerror(errno)); } return res; } @@ -1203,14 +1303,12 @@ /* returns TRUE on failure, FALSE on success */ static int create_addr(struct sip_pvt *r, char *opeer) { - struct hostent *hp; - struct ast_hostent ahp; struct sip_peer *p; int found=0; char *port; char *callhost; int portno; - char host[256], *hostn; + char *hostn; char peer[256]=""; strncpy(peer, opeer, sizeof(peer) - 1); @@ -1289,32 +1387,29 @@ } } ast_mutex_unlock(&peerl.lock); + + /* Go find host in DNS if not found in peer list (sip.conf) or realtime config */ if (!p && !found) { + struct srvresult *srv; + hostn = peer; if (port) portno = atoi(port); else portno = DEFAULT_SIP_PORT; - if (srvlookup) { - char service[256]; - int tportno; - int ret; - snprintf(service, sizeof(service), "_sip._udp.%s", peer); - ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service); - if (ret > 0) { - hostn = host; - portno = tportno; - } - } - hp = ast_gethostbyname(hostn, &ahp); - if (hp) { + + + srv = find_host(hostn, portno); + if (srv->hp) { strncpy(r->tohost, peer, sizeof(r->tohost) - 1); - memcpy(&r->sa.sin_addr, hp->h_addr, sizeof(r->sa.sin_addr)); - r->sa.sin_port = htons(portno); + memcpy(&r->sa.sin_addr, srv->hp->h_addr, sizeof(r->sa.sin_addr)); + r->sa.sin_port = htons(srv->portno); memcpy(&r->recv, &r->sa, sizeof(r->recv)); + free(srv); return 0; } else { - ast_log(LOG_WARNING, "No such host: %s\n", peer); + ast_log(LOG_WARNING, "SIP call failed: No such host: %s\n", peer); + free(srv); return -1; } } else if (!p) @@ -8539,6 +8634,9 @@ default_fromdomain[0] = '\0'; strncpy(default_useragent, DEFAULT_USERAGENT, sizeof(default_useragent) - 1); strncpy(global_realm, "asterisk", sizeof(global_realm) - 1); + global_outboundproxy[0] = '\0'; /* Reset outbound proxy setting */ + outboundproxyip.sin_family = AF_INET; /* Type of address: IPv4 */ + global_realm[sizeof(global_realm)-1] = '\0'; global_canreinvite = REINVITE_INVITE; videosupport = 0; @@ -8555,6 +8653,22 @@ } else if (!strcasecmp(v->name, "realm")) { strncpy(global_realm, v->value, sizeof(global_realm)-1); global_realm[sizeof(global_realm)-1] = '\0'; + } else if (!strcasecmp(v->name, "outboundproxy")) { + struct srvresult *srv; + + strncpy(global_outboundproxy,v->value, sizeof(global_outboundproxy)-1); + srv = find_host(v->value,5060); /* Use SRV Lookups */ + if (!srv->hp) { + ast_log(LOG_WARNING, "Invalid outboundproxy: %s\n", v->value); + } else { + memcpy(&outboundproxyip.sin_addr, srv->hp->h_addr, sizeof(outboundproxyip.sin_addr)); + outboundproxyip.sin_port = srv->portno; + } + free(srv); + } else if (!strcasecmp(v->name, "outboundproxyport")) { + /* Port needs to be after IP */ + sscanf(v->value, "%i", &format); + outboundproxyip.sin_port = htons(format); } else if (!strcasecmp(v->name, "useragent")) { strncpy(default_useragent, v->value, sizeof(default_useragent)-1); ast_log(LOG_DEBUG, "Setting User Agent Name to %s\n", @@ -8617,8 +8731,8 @@ global_nat = SIP_NAT_NEVER; } else if (!strcasecmp(v->name, "autocreatepeer")) { autocreatepeer = ast_true(v->value); - } else if (!strcasecmp(v->name, "srvlookup")) { - srvlookup = ast_true(v->value); + } else if (!strcasecmp(v->name, "global_srvlookup")) { + global_srvlookup = ast_true(v->value); } else if (!strcasecmp(v->name, "trustrpid")) { global_trustrpid = ast_true(v->value); } else if (!strcasecmp(v->name, "progressinband")) { Index: configs/sip.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/sip.conf.sample,v retrieving revision 1.42 diff -u -r1.42 sip.conf.sample --- configs/sip.conf.sample 12 Nov 2004 03:57:39 -0000 1.42 +++ configs/sip.conf.sample 14 Nov 2004 11:19:06 -0000 @@ -22,6 +22,7 @@ [general] context=default ; Default context for incoming calls + ; from devices not defined in this file ;recordhistory=yes ; Record SIP history by default ; (see sip history / sip no history) ;realm=mydomain.tld ; Realm for digest authentication @@ -36,7 +37,11 @@ ; Disabling DNS SRV lookups disables the ; ability to place SIP calls based on domain ; names to some other SIP users on the Internet +;language=en ; Default language setting for all users/peers +;useragent=Asterisk PBX ; Allows you to change the user agent string + ; This may also be set for individual users/peers +;----------------------------------------------------ADVANCED SETTINGS ;pedantic=yes ; Enable slow, pedantic checking for Pingtel ; and multiline formatted headers for strict ; SIP compatibility (defaults to "no") @@ -53,8 +58,6 @@ ;allow=ilbc ; Note: codec order is respected only in [general] ;musicclass=default ; Sets the default music on hold class for all SIP calls ; This may also be set for individual users/peers -;language=en ; Default language setting for all users/peers - ; This may also be set for individual users/peers ;relaxdtmf=yes ; Relax dtmf handling ;rtptimeout=60 ; Terminate call if 60 seconds of no RTP activity ; when we're not on hold @@ -62,13 +65,6 @@ ; when we're on hold (must be > rtptimeout) ;trustrpid = no ; If Remote-Party-ID should be trusted ;progressinband=no ; If we should generate in-band ringing always -;useragent=Asterisk PBX ; Allows you to change the user agent string -;nat=no ; NAT settings - ; yes = Always ignore info and assume NAT - ; no = Use NAT mode only according to RFC3581 - ; never = Never attempt NAT mode or RFC3581 support - ; route = Assume NAT, don't send rport - ; (work around more UNIDEN bugs) ;promiscredir = no ; If yes, allows 302 or REDIR to non-local SIP address ; Note that promiscredir when redirects are made to the ; local system will cause loops since SIP is incapable @@ -87,6 +83,9 @@ ; ;regcontext=sipregistrations ; +; +;---------------------------------------------------------REGISTRATIONS +; ; Asterisk can register as a SIP user agent to a SIP proxy (provider) ; Format for the register statement is: ; register => user[:secret[:authuser]]@host[:port][/extension] @@ -113,20 +112,38 @@ ; Tip 1: Avoid assigning hostname to a sip.conf section like [provider.com] ; Tip 2: Use separate type=peer and type=user sections for SIP providers ; (instead of type=friend) if you have calls in both directions + +;--------------------------------------------------------NAT TRAVERSAL SUPPORT + ; Note: Some of these settings is configurable + ; on a [device] basis also. + +;nat=no ; NAT settings + ; yes = Always ignore info and assume NAT + ; no = Use NAT mode only according to RFC3581 + ; never = Never attempt NAT mode or RFC3581 support + ; route = Assume NAT, don't send rport + ; (work around more UNIDEN bugs) ;externip = 200.201.202.203 ; Address that we're going to put in outbound SIP messages - ; if we're behind a NAT + ; if Asterisk communicates with devices or proxy servers + ; (service providers) from behind a NAT ; The externip and localnet is used ; when registering and communicating with other proxies ; that we're registered with - ; You may add multiple local networks. A reasonable set of defaults - ; are: -;localnet=192.168.0.0/255.255.0.0; All RFC 1918 addresses are local networks -;localnet=10.0.0.0/255.0.0.0 ; Also RFC1918 -;localnet=172.16.0.0/12 ; Another RFC1918 with CIDR notation -;localnet=169.254.0.0/255.255.0.0 ;Zero conf local network + ; You may add multiple local networks. + ; A reasonable set of defaults are: +;localnet=192.168.0.0/255.255.0.0 ; All RFC 1918 addresses are local networks +;localnet=10.0.0.0/255.0.0.0 ; RFC1918 +;localnet=172.16.0.0/12 ; RFC1918 with CIDR notation +;localnet=169.254.0.0/255.255.0.0 ; Zero conf local network + +;outboundproxy=hostname.domain.tld ; Name of outbound proxy to use for nat traversal +;outboundproxyport ; Port address of outbound proxy + ; If you configure an outbound proxy, all packets + ; with a destination outside of your localnet= + ; setting will be sent to the outbound proxy ;----------------------------------------------------------------------------------- ; Users and peers have different settings available. Friends have all settings,