Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.836 diff -u -r1.836 chan_sip.c --- channels/chan_sip.c 2 Sep 2005 19:24:32 -0000 1.836 +++ channels/chan_sip.c 6 Sep 2005 07:06:03 -0000 @@ -364,6 +364,9 @@ /* Protect the interface list (of sip_pvt's) */ AST_MUTEX_DEFINE_STATIC(iflock); +/* Protect the domain list */ +AST_MUTEX_DEFINE_STATIC(sip_domain_lock); + /* Protect the monitoring thread, so only one process can kill or start it, and not when it's doing something critical. */ AST_MUTEX_DEFINE_STATIC(netlock); @@ -453,6 +456,22 @@ char hop[0]; }; +/* sip_domains: Our domains */ +struct sip_domains { + struct sip_domains *next; + char domain[MAXHOSTNAMELEN]; + char context[AST_MAX_EXTENSION]; + int mode; +}; + +#define SIP_DOMAIN_AUTO 1 +#define SIP_DOMAIN_CONFIG 2 +#define SIP_DOMAIN_INFO 99 /* Info text, no domain */ + +struct sip_domains *our_sip_domains = NULL; +int use_sip_domains; +int allow_external_invites; + /* sip_history: Structure for saving transactions within a SIP dialog */ struct sip_history { char event[80]; @@ -860,6 +879,7 @@ static int clear_realm_authentication(struct sip_auth *authlist); /* Clear realm authentication list (at reload) */ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno); /* Add realm authentication in list */ static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, char *realm); /* Find authentication for a specific realm */ +static int check_sip_domain(char *domain, char *context, int contextlen); /* Check if domain is one of our local domains */ static void append_date(struct sip_request *req); /* Append date to SIP packet */ static int determine_firstline_parts(struct sip_request *req); static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ @@ -5932,6 +5952,7 @@ char iabuf[INET_ADDRSTRLEN]; char *name, *c; char *t; + char *domain; /* Terminate URI */ t = uri; @@ -5957,8 +5978,20 @@ } /* Strip off the domain name */ c = strchr(name, '@'); - if (c) + if (c) { + char *colon; *c = '\0'; + domain = ++c; + colon = strchr(domain, ':'); /* Remove :port */ + if (colon) + *colon = '\0'; + if (use_sip_domains) { + if (!check_sip_domain(domain, NULL, 0)) { + transmit_response(p, "404 Not found (unknown domain)", &p->initreq); + return -3; + } + } + } ast_copy_string(p->exten, name, sizeof(p->exten)); build_contact(p); peer = find_peer(name, NULL, 1); @@ -6033,6 +6066,7 @@ } if (peer) ASTOBJ_UNREF(peer,sip_destroy_peer); + return res; } @@ -6067,8 +6101,8 @@ /*--- get_destination: Find out who the call is for --*/ static int get_destination(struct sip_pvt *p, struct sip_request *oreq) { - char tmp[256] = "", *c, *a; - char tmpf[256], *fr; + char tmp[256] = "", *uri, *a; + char tmpf[256], *from; struct sip_request *req; req = oreq; @@ -6076,60 +6110,87 @@ req = &p->initreq; if (req->rlPart2) ast_copy_string(tmp, req->rlPart2, sizeof(tmp)); + uri = get_in_brackets(tmp); ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf)); - if (pedanticsipchecking) { - ast_uri_decode(tmp); - ast_uri_decode(tmpf); - } - - fr = get_in_brackets(tmpf); - c = get_in_brackets(tmp); + from = get_in_brackets(tmpf); - if (strncmp(c, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", c); + if (strncmp(uri, "sip:", 4)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", uri); return -1; } - c += 4; - if (!ast_strlen_zero(fr)) { - if (strncmp(fr, "sip:", 4)) { - ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", fr); + uri += 4; + if (!ast_strlen_zero(from)) { + if (strncmp(from, "sip:", 4)) { + ast_log(LOG_WARNING, "Huh? Not a SIP header (%s)?\n", from); return -1; } - fr += 4; + from += 4; } else - fr = NULL; - if ((a = strchr(c, '@'))) { + from = NULL; + + if (pedanticsipchecking) { + ast_uri_decode(uri); + ast_uri_decode(from); + } + + /* Get the target domain */ + if ((a = strchr(uri, '@'))) { + char *colon; *a = '\0'; a++; + colon = strchr(a, ':'); /* Remove :port */ + if (colon) + *colon = '\0'; ast_copy_string(p->domain, a, sizeof(p->domain)); } - if ((a = strchr(c, ';'))) { + /* Skip any options */ + if ((a = strchr(uri, ';'))) { *a = '\0'; } - if (fr) { - if ((a = strchr(fr, ';'))) + + if (use_sip_domains) { + char domain_context[AST_MAX_EXTENSION]; + domain_context[0] = '\0'; + if (!check_sip_domain(p->domain, domain_context, AST_MAX_EXTENSION)) { + if (allow_external_invites && (req->method == SIP_INVITE || req->method == SIP_REFER)) { + if (sipdebug) + ast_log(LOG_DEBUG, "Got SIP %s to non-local domain \"%s\". Allowing session.\n", sip_methods[req->method].text, p->domain); + } else { + ast_log(LOG_NOTICE, "Got SIP Packet to non-local domain \"%s\". Refusing session\n", p->domain); + return -2; + } + } + /* If we have a context defined, overwrite the context with this context */ + if (domain_context[0]) { + ast_copy_string(p->context, domain_context, sizeof(p->context)); + } + } + + if (from) { + if ((a = strchr(from, ';'))) *a = '\0'; - if ((a = strchr(fr, '@'))) { + if ((a = strchr(from, '@'))) { *a = '\0'; ast_copy_string(p->fromdomain, a + 1, sizeof(p->fromdomain)); } else - ast_copy_string(p->fromdomain, fr, sizeof(p->fromdomain)); + ast_copy_string(p->fromdomain, from, sizeof(p->fromdomain)); } - if (pedanticsipchecking) - ast_uri_decode(c); if (sip_debug_test_pvt(p)) - ast_verbose("Looking for %s in %s\n", c, p->context); - if (ast_exists_extension(NULL, p->context, c, 1, fr) || - !strcmp(c, ast_pickup_ext())) { + ast_verbose("Looking for %s in %s (domain %s)\n", uri, p->context, p->domain); + + /* Return 0 if we have a matching extension */ + if (ast_exists_extension(NULL, p->context, uri, 1, from) || + !strcmp(uri, ast_pickup_ext())) { if (!oreq) - ast_copy_string(p->exten, c, sizeof(p->exten)); + ast_copy_string(p->exten, uri, sizeof(p->exten)); return 0; } - if (ast_canmatch_extension(NULL, p->context, c, 1, fr) || - !strncmp(c, ast_pickup_ext(),strlen(c))) { + /* Return 1 for overlap dialling support */ + if (ast_canmatch_extension(NULL, p->context, uri, 1, from) || + !strncmp(uri, ast_pickup_ext(),strlen(uri))) { return 1; } @@ -7313,6 +7374,25 @@ } +/*--- sip_show_domains: CLI command to list local domains */ +#define FORMAT "%-40.40s %-20.20s %-16.16s\n" +static int sip_show_domains(int fd, int argc, char *argv[]) +{ + struct sip_domains *sd = our_sip_domains; + ast_cli(fd, FORMAT, "Our local SIP domains:", "Context", "Set by"); + if (!use_sip_domains) { + ast_cli(fd, " SIP Domain support disabled.\n\n"); + return RESULT_SUCCESS; + } + while (sd) { + ast_cli(fd, FORMAT, sd->domain, sd->context?sd->context:"(default)", sd->mode==1?"[Automatic]":(sd->mode==2?"[Configuration]":"[Error msg]")); + sd = sd->next; + } + ast_cli(fd, "\n"); + return RESULT_SUCCESS; +} +#undef FORMAT + static char mandescr_show_peer[] = "Description: Show one SIP peer with details on current status.\n" " The XML format is under development, feedback welcome! /oej\n" @@ -7648,6 +7728,8 @@ ast_cli(fd, " AutoCreatePeer: %s\n", autocreatepeer ? "Yes" : "No"); ast_cli(fd, " Allow unknown access: %s\n", global_allowguest ? "Yes" : "No"); ast_cli(fd, " Promsic. redir: %s\n", ast_test_flag(&global_flags, SIP_PROMISCREDIR) ? "Yes" : "No"); + ast_cli(fd, " SIP domain support: %s\n", use_sip_domains ? "Yes" : "No"); + ast_cli(fd, " Call to non-local dom.: %s\n", allow_external_invites ? "Yes" : "No"); ast_cli(fd, " URI user is phone no: %s\n", ast_test_flag(&global_flags, SIP_USEREQPHONE) ? "Yes" : "No"); ast_cli(fd, " Our auth realm %s\n", global_realm); ast_cli(fd, " Realm. auth: %s\n", authl ? "Yes": "No"); @@ -8524,7 +8606,10 @@ return 0; } - +static char show_domains_usage[] = +"Usage: sip show domains\n" +" Lists all configured SIP local domains.\n" +" Asterisk only responds to SIP messages to local domains.\n"; static char notify_usage[] = "Usage: sip notify [...]\n" @@ -8664,6 +8749,32 @@ .read = func_header_read, }; +/*--- function_check_sipdomain: Dial plan function to check if domain is local */ +static char *func_check_sipdomain(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) +{ + if (!data || ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "CHECKSIPDOMAIN requires an argument - A domain name\n"); + return buf; + } + if (check_sip_domain(data, NULL, 0)) + ast_copy_string(buf, data, len); + else + buf[0] = '\0'; + return buf; +} + +static struct ast_custom_function checksipdomain_function = { + .name = "CHECKSIPDOMAIN", + .synopsis = "Checks if domain is a local domain", + .syntax = "CHECKSIPDOMAIN()", + .read = func_check_sipdomain, + .desc = "This function checks if the domain in the argument is configured\n" + "as a local SIP domain that this Asterisk server is configured to handle.\n" + "Returns the domain name if it is locally handled, otherwise an empty string.\n" + "Check the domain= configuration in sip.conf\n", +}; + + /*--- function_sippeer: ${SIPPEER()} Dialplan function - reads peer data */ static char *function_sippeer(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) { @@ -8738,10 +8849,10 @@ /* Structure to declare a dialplan function: SIPPEER */ struct ast_custom_function sippeer_function = { - .name = "SIPPEER", - .synopsis = "Gets SIP peer information", - .syntax = "SIPPEER([:item])", - .read = function_sippeer, + .name = "SIPPEER", + .synopsis = "Gets SIP peer information", + .syntax = "SIPPEER([:item])", + .read = function_sippeer, .desc = "Valid items are:\n" "- ip (default) The IP address.\n" "- mailbox The configured mailbox.\n" @@ -8968,6 +9079,7 @@ if (sscanf(tmptmp + 8, "%d;", &expires) != 1) expires = 0; } + } if (!expires) expires=atoi(get_header(req, "expires")); @@ -9663,6 +9775,7 @@ } /* Get destination right away */ gotdest = get_destination(p, NULL); + get_rdnis(p, NULL); extract_uri(p, req); build_contact(p); @@ -10114,7 +10227,7 @@ copy_request(&p->initreq, req); check_via(p, req); if ((res = register_verify(p, sin, req, e, ignore)) < 0) - ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n", get_header(req, "To"), ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), (res == -1) ? "Wrong password" : "Username/auth name mismatch"); + ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n", get_header(req, "To"), ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), (res == -1) ? "Wrong password" : (res == -2 ? "Username/auth name mismatch" : "Not a local SIP domain")); if (res < 1) { /* Destroy the session, but keep us around for just a bit in case they don't get our 200 OK */ @@ -10910,6 +11023,78 @@ return res; } +/*--- add_sip_domain: Add SIP domain to list of domains we are responsible for */ +static int add_sip_domain(char *newdomain, int mode, char *context) +{ + struct sip_domains *sd; + + sd = malloc(sizeof(struct sip_domains)); + if (!sd) { + ast_log(LOG_ERROR, "Allocation of sip_domains structure failed, Out of memory\n"); + return 0; + } + memset(sd, 0, sizeof(struct sip_domains)); + + if (!ast_strlen_zero(newdomain)) + ast_copy_string(sd->domain, newdomain, sizeof(sd->domain)); + else { + ast_log(LOG_WARNING, "Zero length domain\n"); + free(sd); + return 1; + } + if (context && !ast_strlen_zero(context)) { + char *c = context; + c = ast_skip_blanks(c); + ast_copy_string(sd->context, context, sizeof(sd->context)); + } + sd->mode = mode; + + /* Add in front of list */ + ast_mutex_lock(&sip_domain_lock); + sd->next = our_sip_domains; + our_sip_domains = sd; + ast_mutex_unlock(&sip_domain_lock); + if (sipdebug) + ast_log(LOG_DEBUG, "Added local SIP domain %s\n", newdomain); + return 1; +} + +/*--- check_sip_domain: Check if domain part of uri is local to our server */ +static int check_sip_domain(char *domain, char *context, int contextlen) +{ + struct sip_domains *sd = our_sip_domains; + + while (sd) { + if (sd->mode != SIP_DOMAIN_INFO && !strcasecmp(sd->domain, domain)) { + /* We've got a match! */ + if (sd->context && contextlen) { + ast_copy_string(context, sd->context, contextlen); + } + return 1; + } + sd = sd->next; + } + return 0; +} + +/*--- clear_sip_domains: Clear our domain list (at reload) */ +static void clear_sip_domains(void) +{ + struct sip_domains *sd = our_sip_domains; + struct sip_domains *next; + + our_sip_domains = NULL; + ast_mutex_lock(&sip_domain_lock); + + while (sd) { + next = sd->next; + free(sd); + sd = next; + } + ast_mutex_unlock(&sip_domain_lock); +} + + /*--- add_realm_authentication: Add realm authentication in list ---*/ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno) { @@ -11433,6 +11618,8 @@ int oldport = ntohs(bindaddr.sin_port); char iabuf[INET_ADDRSTRLEN]; struct ast_flags dummy; + int auto_sip_domains = 0; + cfg = ast_config_load(config); @@ -11455,6 +11642,8 @@ default_language[0] = '\0'; default_fromdomain[0] = '\0'; default_qualify = 0; + allow_external_invites = 1; /* Allow external invites */ + use_sip_domains = 0; /* Turned off by default */ externhost[0] = '\0'; externexpire = 0; externrefresh = 10; @@ -11641,6 +11830,20 @@ ast_parse_allow_disallow(&prefs, &global_capability, v->value, 1); } else if (!strcasecmp(v->name, "disallow")) { ast_parse_allow_disallow(&prefs, &global_capability, v->value, 0); + } else if (!strcasecmp(v->name, "allowexternalinvites")) { + allow_external_invites = ast_true(v->value); + } else if (!strcasecmp(v->name, "autodomain")) { + if((auto_sip_domains = ast_true(v->value))) + use_sip_domains = 1; + } else if (!strcasecmp(v->name, "domain")) { + char *domain = v->value; + char *context = strchr(domain, ','); + if (context) { + *context = '\0'; + context++; + } + add_sip_domain(domain, SIP_DOMAIN_CONFIG, context); + use_sip_domains = 1; } else if (!strcasecmp(v->name, "register")) { sip_register(v->value, v->lineno); } else if (!strcasecmp(v->name, "tos")) { @@ -11669,6 +11872,10 @@ */ v = v->next; } + if (!allow_external_invites && !use_sip_domains) { + ast_log(LOG_WARNING,"To disallow external INVITEs, you need to configure local SIP domains.\n"); + allow_external_invites = 1; + } /* Build list of authentication to various SIP realms, i.e. service providers */ v = ast_variable_browse(cfg, "authentication"); @@ -11738,7 +11945,7 @@ sipsock = -1; } else { if (option_verbose > 1) { - ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on %s:%d\n", + ast_verbose(VERBOSE_PREFIX_2 "SIP Listening on %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), bindaddr.sin_addr), ntohs(bindaddr.sin_port)); ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos); } @@ -11749,6 +11956,40 @@ } ast_mutex_unlock(&netlock); + /* Add default domains - host name, IP address and IP:port */ + /* Only do this if user added any sip domain with "localdomains" */ + /* In order to *not* break backwards compatibility */ + /* Some phones address us at IP only, some with additional port number */ + if (auto_sip_domains) { + char temp[MAXHOSTNAMELEN]; + + /* First our default IP address */ + if (bindaddr.sin_addr.s_addr) { + ast_inet_ntoa(temp, sizeof(temp), bindaddr.sin_addr); + add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL); + } else { + ast_log(LOG_NOTICE, "Can't add multiple IP addresses to domain list (bindaddr=0.0.0.0), please add IP addr to domain manually.\n"); + add_sip_domain("Error: Bindaddr=0.0.0.0, can't autoconfigure SIP domains", SIP_DOMAIN_INFO, NULL); + } + + /* Our extern IP address, if configured */ + if (externip.sin_addr.s_addr) { + ast_inet_ntoa(temp, sizeof(temp), externip.sin_addr); + add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL); + + } + + /* Extern host name (NAT traversal support) */ + if (!ast_strlen_zero(externhost)) { + add_sip_domain(externhost, SIP_DOMAIN_AUTO, NULL); + } + + /* Our host name */ + if(!gethostname(temp, sizeof(temp))) { + add_sip_domain(temp, SIP_DOMAIN_AUTO, NULL); + } + } + /* Release configuration from memory */ ast_config_destroy(cfg); @@ -12116,6 +12357,7 @@ static int sip_do_reload(void) { clear_realm_authentication(authl); + clear_sip_domains(); authl = NULL; ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user); @@ -12152,7 +12394,6 @@ return sip_reload(0, 0, NULL); } -// static struct ast_cli_entry cli_sip_reload = static struct ast_cli_entry my_clis[] = { { { "sip", "notify", NULL }, sip_notify, "Send a notify packet to a SIP peer", notify_usage, complete_sipnotify }, { { "sip", "show", "objects", NULL }, sip_show_objects, "Show all SIP object allocations", show_objects_usage }, @@ -12162,6 +12403,7 @@ { { "sip", "show", "channels", NULL }, sip_show_channels, "Show active SIP channels", show_channels_usage}, { { "sip", "show", "channel", NULL }, sip_show_channel, "Show detailed SIP channel info", show_channel_usage, complete_sipch }, { { "sip", "show", "history", NULL }, sip_show_history, "Show SIP dialog history", show_history_usage, complete_sipch }, + { { "sip", "show", "domains", NULL }, sip_show_domains, "List our local SIP domains.", show_domains_usage }, { { "sip", "show", "settings", NULL }, sip_show_settings, "Show SIP global settings", show_settings_usage }, { { "sip", "debug", NULL }, sip_do_debug, "Enable SIP debugging", debug_usage }, { { "sip", "debug", "ip", NULL }, sip_do_debug, "Enable SIP debugging on IP", debug_usage }, @@ -12225,6 +12467,7 @@ ast_custom_function_register(&sip_header_function); ast_custom_function_register(&sippeer_function); ast_custom_function_register(&sipchaninfo_function); + ast_custom_function_register(&checksipdomain_function); /* Register manager commands */ ast_manager_register2("SIPpeers", EVENT_FLAG_SYSTEM, manager_sip_show_peers, @@ -12251,12 +12494,13 @@ ast_custom_function_unregister(&sipchaninfo_function); ast_custom_function_unregister(&sippeer_function); ast_custom_function_unregister(&sip_header_function); + ast_custom_function_unregister(&checksipdomain_function); ast_unregister_application(app_dtmfmode); ast_unregister_application(app_sipaddheader); ast_unregister_application(app_sipgetheader); - ast_cli_unregister_multiple(my_clis, sizeof(my_clis)/ sizeof(my_clis[0])); + ast_cli_unregister_multiple(my_clis, sizeof(my_clis) / sizeof(my_clis[0])); ast_rtp_proto_unregister(&sip_rtp); @@ -12323,6 +12567,7 @@ ASTOBJ_CONTAINER_DESTROY(®l); clear_realm_authentication(authl); + clear_sip_domains(); close(sipsock); return 0;