diff options
author | kpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-09-26 23:07:50 +0000 |
---|---|---|
committer | kpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b> | 2005-09-26 23:07:50 +0000 |
commit | 8f15db261b2b258d15e5a8be8cfb9a41e6f3a95a (patch) | |
tree | 9e863226d26545658c92ac3650eb15c889659bca /channels/chan_sip.c | |
parent | 5478cd2e6166f116850da04f97abed3d2377fa9d (diff) |
add basic SIP domain support (issue #4466, with major mods)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@6668 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/chan_sip.c')
-rwxr-xr-x | channels/chan_sip.c | 334 |
1 files changed, 291 insertions, 43 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 8a7d00d1f..7c57d37ac 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -74,6 +74,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/astobj.h" #include "asterisk/dnsmgr.h" #include "asterisk/devicestate.h" +#include "asterisk/linkedlists.h" + #ifdef OSP_SUPPORT #include "asterisk/astosp.h" #endif @@ -464,6 +466,22 @@ struct sip_route { char hop[0]; }; +enum domain_mode { + SIP_DOMAIN_AUTO, + SIP_DOMAIN_CONFIG, +}; + +struct domain { + char domain[MAXHOSTNAMELEN]; + char context[AST_MAX_EXTENSION]; + enum domain_mode mode; + AST_LIST_ENTRY(domain) list; +}; + +static AST_LIST_HEAD_STATIC(domain_list, domain); + +int allow_external_invites; + /* sip_history: Structure for saving transactions within a SIP dialog */ struct sip_history { char event[80]; @@ -870,6 +888,7 @@ static int sip_senddigit(struct ast_channel *ast, char digit); 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(const char *domain, char *context, size_t len); /* 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 */ @@ -6075,6 +6094,7 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si char iabuf[INET_ADDRSTRLEN]; char *name, *c; char *t; + char *domain; /* Terminate URI */ t = uri; @@ -6098,10 +6118,21 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si name = c; ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr)); } + /* Strip off the domain name */ - c = strchr(name, '@'); - if (c) - *c = '\0'; + if ((c = strchr(name, '@'))) { + *c++ = '\0'; + domain = c; + if ((c = strchr(domain, ':'))) /* Remove :port */ + *c = '\0'; + if (!AST_LIST_EMPTY(&domain_list)) { + 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); @@ -6192,6 +6223,7 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si } if (peer) ASTOBJ_UNREF(peer,sip_destroy_peer); + return res; } @@ -6226,8 +6258,8 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq) /*--- 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; @@ -6235,60 +6267,84 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq) 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 (!AST_LIST_EMPTY(&domain_list)) { + char domain_context[AST_MAX_EXTENSION]; + + domain_context[0] = '\0'; + if (!check_sip_domain(p->domain, domain_context, sizeof(domain_context))) { + if (allow_external_invites && (req->method == SIP_INVITE || req->method == SIP_REFER)) { + ast_log(LOG_DEBUG, "Got SIP %s to non-local domain '%s'; refusing request.\n", sip_methods[req->method].text, p->domain); + return -2; + } + } + /* If we have a context defined, overwrite the original context */ + if (!ast_strlen_zero(domain_context)) + 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; } @@ -7472,6 +7528,39 @@ static void print_codec_to_cli(int fd, struct ast_codec_pref *pref) ast_cli(fd, "none"); } +static const char *domain_mode_to_text(const enum domain_mode mode) +{ + switch (mode) { + case SIP_DOMAIN_AUTO: + return "[Automatic]"; + case SIP_DOMAIN_CONFIG: + return "[Configured]"; + } + + return ""; +} + +/*--- 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 domain *d; + + if (AST_LIST_EMPTY(&domain_list)) { + ast_cli(fd, "SIP Domain support not enabled.\n\n"); + return RESULT_SUCCESS; + } else { + ast_cli(fd, FORMAT, "Our local SIP domains:", "Context", "Set by"); + AST_LIST_LOCK(&domain_list); + AST_LIST_TRAVERSE(&domain_list, d, list) + ast_cli(fd, FORMAT, d->domain, ast_strlen_zero(d->context) ? "(default)": d->context, + domain_mode_to_text(d->mode)); + AST_LIST_UNLOCK(&domain_list); + 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" @@ -7806,6 +7895,8 @@ static int sip_show_settings(int fd, int argc, char *argv[]) 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", AST_LIST_EMPTY(&domain_list) ? "No" : "Yes"); + 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"); @@ -8682,7 +8773,10 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d 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 <type> <peer> [<peer>...]\n" @@ -8822,6 +8916,32 @@ static struct ast_custom_function sip_header_function = { .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(<domain|IP>)", + .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) { @@ -8896,10 +9016,10 @@ static char *function_sippeer(struct ast_channel *chan, char *cmd, char *data, c /* Structure to declare a dialplan function: SIPPEER */ struct ast_custom_function sippeer_function = { - .name = "SIPPEER", - .synopsis = "Gets SIP peer information", - .syntax = "SIPPEER(<peername>[:item])", - .read = function_sippeer, + .name = "SIPPEER", + .synopsis = "Gets SIP peer information", + .syntax = "SIPPEER(<peername>[:item])", + .read = function_sippeer, .desc = "Valid items are:\n" "- ip (default) The IP address.\n" "- mailbox The configured mailbox.\n" @@ -9269,6 +9389,7 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str if (sscanf(tmptmp + 8, "%d;", &expires) != 1) expires = 0; } + } if (!expires) expires=atoi(get_header(req, "expires")); @@ -9933,6 +10054,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int } /* Get destination right away */ gotdest = get_destination(p, NULL); + get_rdnis(p, NULL); extract_uri(p, req); build_contact(p); @@ -10382,7 +10504,7 @@ static int handle_request_register(struct sip_pvt *p, struct sip_request *req, i 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 */ @@ -11171,6 +11293,73 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask return res; } +/*--- add_sip_domain: Add SIP domain to list of domains we are responsible for */ +static int add_sip_domain(const char *domain, const enum domain_mode mode, const char *context) +{ + struct domain *d; + + if (!domain || ast_strlen_zero(domain)) { + ast_log(LOG_WARNING, "Zero length domain.\n"); + return 1; + } + + d = calloc(1, sizeof(*d)); + if (!d) { + ast_log(LOG_ERROR, "Allocation of domain structure failed, Out of memory\n"); + return 0; + } + + ast_copy_string(d->domain, domain, sizeof(d->domain)); + + if (context && !ast_strlen_zero(context)) + ast_copy_string(d->context, context, sizeof(d->context)); + + d->mode = mode; + + AST_LIST_LOCK(&domain_list); + AST_LIST_INSERT_TAIL(&domain_list, d, list); + AST_LIST_UNLOCK(&domain_list); + + if (sipdebug) + ast_log(LOG_DEBUG, "Added local SIP domain '%s'\n", domain); + + return 1; +} + +/*--- check_sip_domain: Check if domain part of uri is local to our server */ +static int check_sip_domain(const char *domain, char *context, size_t len) +{ + struct domain *d; + int result = 0; + + AST_LIST_LOCK(&domain_list); + AST_LIST_TRAVERSE(&domain_list, d, list) { + if (strcasecmp(d->domain, domain)) + continue; + + if (len && !ast_strlen_zero(d->context)) + ast_copy_string(context, d->context, len); + + result = 1; + break; + } + AST_LIST_UNLOCK(&domain_list); + + return result; +} + +/*--- clear_sip_domains: Clear our domain list (at reload) */ +static void clear_sip_domains(void) +{ + struct domain *d; + + AST_LIST_LOCK(&domain_list); + while ((d = AST_LIST_REMOVE_HEAD(&domain_list, list))) + free(d); + AST_LIST_UNLOCK(&domain_list); +} + + /*--- add_realm_authentication: Add realm authentication in list ---*/ static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno) { @@ -11689,6 +11878,8 @@ static int reload_config(void) int oldport = ntohs(bindaddr.sin_port); char iabuf[INET_ADDRSTRLEN]; struct ast_flags dummy; + int auto_sip_domains = 0; + cfg = ast_config_load(config); @@ -11711,6 +11902,7 @@ static int reload_config(void) default_language[0] = '\0'; default_fromdomain[0] = '\0'; default_qualify = 0; + allow_external_invites = 1; /* Allow external invites */ externhost[0] = '\0'; externexpire = 0; externrefresh = 10; @@ -11897,6 +12089,23 @@ static int reload_config(void) 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")) { + auto_sip_domains = ast_true(v->value); + } else if (!strcasecmp(v->name, "domain")) { + char *domain = ast_strdupa(v->value); + char *context = strchr(domain, ','); + + if (context) + *context++ = '\0'; + + if (ast_strlen_zero(domain)) + ast_log(LOG_WARNING, "Empty domain specified at line %d\n", v->lineno); + else if (context && ast_strlen_zero(context)) + ast_log(LOG_WARNING, "Empty context specified at line %d for domain '%s'\n", v->lineno, domain); + else + add_sip_domain(ast_strip(domain), SIP_DOMAIN_CONFIG, context ? ast_strip(context) : ""); } else if (!strcasecmp(v->name, "register")) { sip_register(v->value, v->lineno); } else if (!strcasecmp(v->name, "tos")) { @@ -11925,6 +12134,11 @@ static int reload_config(void) */ v = v->next; } + + if (!allow_external_invites && AST_LIST_EMPTY(&domain_list)) { + 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"); @@ -11994,7 +12208,7 @@ static int reload_config(void) 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); } @@ -12005,6 +12219,36 @@ static int reload_config(void) } 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 wildcard IP address to domain list, please add IP address to domain manually.\n"); + } + + /* 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); @@ -12372,6 +12616,7 @@ static void sip_send_all_registers(void) static int sip_do_reload(void) { clear_realm_authentication(authl); + clear_sip_domains(); authl = NULL; ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user); @@ -12408,7 +12653,6 @@ int reload(void) 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 }, @@ -12418,6 +12662,7 @@ static struct ast_cli_entry my_clis[] = { { { "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 }, @@ -12480,6 +12725,7 @@ int load_module() 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, @@ -12506,12 +12752,13 @@ int unload_module() 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); @@ -12578,6 +12825,7 @@ int unload_module() ASTOBJ_CONTAINER_DESTROY(®l); clear_realm_authentication(authl); + clear_sip_domains(); close(sipsock); return 0; |