diff -Nru a/channels/chan_sip.c b/channels/chan_sip.c --- a/channels/chan_sip.c 2005-03-11 09:10:32 -07:00 +++ b/channels/chan_sip.c 2005-03-11 09:10:32 -07:00 @@ -573,6 +573,33 @@ int recheck; } regl; +/* Structure to hold the parsed elements of a SIP URI */ +/* The entire URI is stored in the data field, the other + fields contain pointers to the relevant parts of the + data field. +*/ +struct parsed_uri { + char *username; + char *password; + char *host; + char *port; + char *parameters; + char *headers; + char data[0]; +}; + +/* Structure to hold the parsed elements of a SIP header */ +/* The header may contain a name (optionally quoted), will + contain a URI, and may contain parameters following + the URI + Note: we abuse the "data" field in the embedded parsed_uri + structure to hold the entire header data +*/ +struct parsed_name_and_uri { + char *name; + char *parameters; + struct parsed_uri uri; +}; static int __sip_do_register(struct sip_registry *r); @@ -647,6 +674,125 @@ .send_text = sip_sendtext, }; +static void __parse_uri(struct parsed_uri *result, char *parse) +{ + char *field; + + parse = ast_strip(parse); + + if (strncmp(parse, "sip:", 4)) + ast_log(LOG_NOTICE, "SIP URI missing 'sip:' prefix, using it anyway (%s)\n", parse); + else + parse += 4; + + field = strsep(&parse, "@"); + + /* if parse != NULL, a delimiter was found, so field points to the + username/password portion of the URI; otherwise, reset parse to + point to the beginning of the URI + */ + if (parse) { + result->username = strsep(&field, ":"); + result->password = field; + } else + parse = field; + + /* the host portion is terminated by parameters */ + result->host = strsep(&parse, ";"); + /* the parameter portion is terminated by headers */ + result->parameters = strsep(&parse, "?"); + result->headers = parse; + + /* split the host portion into host/port */ + field = result->host; + result->host = strsep(&field, ":"); + result->port = field; +} + +static struct parsed_uri *parse_uri(const char *uri) +{ + struct parsed_uri *result = NULL; + + if (!uri || ast_strlen_zero(uri)) + return NULL; + + result = malloc(sizeof(*result) + strlen(uri) + 1); + if (!result) + return NULL; + + memset(result, 0, sizeof(*result)); + memcpy(result->data, uri, strlen(uri)); + + __parse_uri(result, result->data); + + return result; +} + +static struct parsed_name_and_uri *parse_name_and_uri(const char *header) +{ + struct parsed_name_and_uri *result = NULL; + char *parse; + char *field; + + if (!header || ast_strlen_zero(header)) + return NULL; + + result = malloc(sizeof(*result) + strlen(header) + 1); + if (!result) + return NULL; + + memset(result, 0, sizeof(*result)); + memcpy(result->uri.data, header, strlen(header)); + + parse = result->uri.data; + + field = strsep(&parse, "<"); + + /* if parse != NULL, we found an left-angle bracket, + so there may be a name in the header + */ + if (parse) { + field = ast_strip(field); + if (*field == '\"') { + *field++; + result->name = strsep(&field, "\""); + /* if field is now NULL, no closing double-quote was found, + so this header is formatted improperly and the name should + be ignored + */ + if (!field) + result->name = NULL; + } else + result->name = field; + /* 'parse' points to the remainder of the header containing the URI + and parameters, so continue parsing */ + field = strsep(&parse, ">"); + /* if parse is NULL, no right-angle bracket was found, this header is + formatted improperly and should be ignored */ + if (!parse) { + free(result); + return NULL; + } + } + + /* whether a name was found or not, field now points to the + URI portion of the header + */ + __parse_uri(&result->uri, field); + + /* if parse != NULL, it points to any remaining data after the URI, + which will be parameters... remove any whitespace, skip the leading + ';' + */ + if (parse) { + parse = ast_strip(parse); + if (*parse++ == ';') + result->parameters = parse; + } + + return result; +} + /*--- sip_debug_test_addr: See if we pass debug IP filter */ static inline int sip_debug_test_addr(struct sockaddr_in *addr) { @@ -5586,78 +5732,33 @@ return 0; } -/*--- get_calleridname: Get caller id name from SIP headers ---*/ -static char *get_calleridname(char *input,char *output, size_t outputsize) -{ - char *end = strchr(input,'<'); - char *tmp = strchr(input,'\"'); - int bytes = 0; - int maxbytes = outputsize - 1; - - if (!end || (end == input)) return NULL; - /* move away from "<" */ - end--; - /* we found "name" */ - if (tmp && tmp < end) { - end = strchr(tmp+1,'\"'); - if (!end) return NULL; - bytes = (int)(end-tmp-1); - /* protect the output buffer */ - if (bytes > maxbytes) { - bytes = maxbytes; - } - strncpy(output, tmp+1, bytes); /* safe */ - output[maxbytes] = '\0'; - } else { - /* we didn't find "name" */ - /* clear the empty characters in the begining*/ - while(*input && (*input < 33)) - input++; - /* clear the empty characters in the end */ - while(*end && (*end < 33) && end > input) - end--; - if (end >= input) { - bytes = (int)(end-input)+1; - /* protect the output buffer */ - if (bytes > maxbytes) { - bytes = maxbytes; - } - strncpy(output, input, bytes); /* safe */ - output[maxbytes] = '\0'; - } - else - return(NULL); - } - return output; -} - -/*--- get_rpid_num: Get caller id number from Remote-Party-ID header field - * Returns true if number should be restricted (privacy setting found) - * output is set to NULL if no number found +/*--- get_rpid_privacy: Get privacy setting from parsed Remote-Party-ID header */ -static int get_rpid_num(char *input,char *output, int maxlen) +static int get_rpid_privacy(struct parsed_name_and_uri *data) { - char *start; - char *end; + char *parse; + char *field; + char *name; - start = strchr(input,':'); - if (!start) { - output[0] = '\0'; + parse = data->parameters; + if (!parse || ast_strlen_zero(parse)) return 0; - } - start++; - /* we found "number" */ - strncpy(output,start,maxlen-1); - output[maxlen-1] = '\0'; - - end = strchr(output,'@'); - if (end) - *end = '\0'; - else - output[0] = '\0'; - if(strstr(input,"privacy=full") || strstr(input,"privacy=uri")) - return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; + while ((field = strsep(&parse, ";"))) { + if (ast_strlen_zero(field)) + continue; + + name = strsep(&field, "="); + /* if there is no value, this is not a field we care about */ + if (!field) + continue; + + if (strcasecmp(name, "privacy")) + continue; + + if (!strcasecmp(field, "full") || !strcasecmp(field, "uri")) + return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; + } return 0; } @@ -5666,33 +5767,28 @@ /*--- check_user: Check if matching user or peer is defined ---*/ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd, char *uri, int reliable, struct sockaddr_in *sin, int ignore, char *mailbox, int mailboxlen) { - struct sip_user *user; + struct sip_user *user = NULL; struct sip_peer *peer; - char *of, from[256] = "", *c; - char *rpid,rpid_num[50]; char iabuf[INET_ADDRSTRLEN]; int res = 0; char *t; - char calleridname[50]; int debug=sip_debug_test_addr(sin); struct ast_variable *tmpvar = NULL, *v = NULL; + struct parsed_name_and_uri *parsed_from; + struct parsed_name_and_uri *parsed_rpid; + + parsed_from = parse_name_and_uri(get_header(req, "From")); + if (!parsed_from) + return 0; + + parsed_rpid = parse_name_and_uri(get_header(req, "Remote-Party-ID")); /* Terminate URI */ t = uri; while(*t && (*t > 32) && (*t != ';')) t++; *t = '\0'; - of = get_header(req, "From"); - strncpy(from, of, sizeof(from) - 1); - memset(calleridname,0,sizeof(calleridname)); - get_calleridname(from, calleridname, sizeof(calleridname)); - - rpid = get_header(req, "Remote-Party-ID"); - memset(rpid_num,0,sizeof(rpid_num)); - if(!ast_strlen_zero(rpid)) - p->callingpres = get_rpid_num(rpid,rpid_num, sizeof(rpid_num)); - of = ditch_braces(from); if (ast_strlen_zero(p->exten)) { t = uri; if (!strncmp(t, "sip:", 4)) @@ -5704,98 +5800,92 @@ if (ast_strlen_zero(p->our_contact)) build_contact(p); } - if (strncmp(of, "sip:", 4)) { - ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); - } else - of += 4; - /* Get just the username part */ - if ((c = strchr(of, '@'))) - *c = '\0'; - if ((c = strchr(of, ':'))) - *c = '\0'; - strncpy(p->cid_num, of, sizeof(p->cid_num) - 1); - ast_shrink_phone_number(p->cid_num); - if (*calleridname) - strncpy(p->cid_name, calleridname, sizeof(p->cid_name) - 1); - if (ast_strlen_zero(of)) - return 0; - user = find_user(of, 1); - /* Find user based on user name in the from header */ - if (!mailbox && user && ast_apply_ha(user->ha, sin)) { - ast_copy_flags(p, user, SIP_TRUSTRPID | SIP_USECLIENTCODE | SIP_NAT | SIP_PROG_INBAND | SIP_OSPAUTH); - /* copy channel vars */ - for (v = user->chanvars ; v ; v = v->next) { - if((tmpvar = ast_variable_new(v->name, v->value))) { - tmpvar->next = p->chanvars; - p->chanvars = tmpvar; - } - } - p->prefs = user->prefs; - /* replace callerid if rpid found, and not restricted */ - if(!ast_strlen_zero(rpid_num) && ast_test_flag(p, SIP_TRUSTRPID)) { - if (*calleridname) - strncpy(p->cid_name, calleridname, sizeof(p->cid_name) - 1); - strncpy(p->cid_num, rpid_num, sizeof(p->cid_num) - 1); - ast_shrink_phone_number(p->cid_num); - } - - if (p->rtp) { - ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); - ast_rtp_setnat(p->rtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); - } - if (p->vrtp) { - ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); - ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); - } - if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, user->md5secret, cmd, uri, reliable, ignore))) { - sip_cancel_destroy(p); - ast_copy_flags(p, user, SIP_PROMISCREDIR | SIP_DTMF | SIP_REINVITE); - /* If we have a call limit, set flag */ - if (user->incominglimit) - ast_set_flag(p, SIP_CALL_LIMIT); - if (!ast_strlen_zero(user->context)) - strncpy(p->context, user->context, sizeof(p->context) - 1); - if (!ast_strlen_zero(user->cid_num) && !ast_strlen_zero(p->cid_num)) { - strncpy(p->cid_num, user->cid_num, sizeof(p->cid_num) - 1); + + if (parsed_from->uri.username) { + strncpy(p->cid_num, parsed_from->uri.username, sizeof(p->cid_num) - 1); + ast_shrink_phone_number(p->cid_num); + user = find_user(parsed_from->uri.username, 1); + if (!mailbox && user && ast_apply_ha(user->ha, sin)) { + ast_copy_flags(p, user, SIP_TRUSTRPID | SIP_USECLIENTCODE | SIP_NAT | SIP_PROG_INBAND | SIP_OSPAUTH); + /* copy channel vars */ + for (v = user->chanvars ; v ; v = v->next) { + if((tmpvar = ast_variable_new(v->name, v->value))) { + tmpvar->next = p->chanvars; + p->chanvars = tmpvar; + } + } + p->prefs = user->prefs; + + /* set cid_num, cid_name and callingpres from rpid if enabled */ + if (parsed_rpid && ast_test_flag(p, SIP_TRUSTRPID)) { + p->callingpres = get_rpid_privacy(parsed_rpid); + if (parsed_rpid->name) + strncpy(p->cid_name, parsed_rpid->name, sizeof(p->cid_name) - 1); + else if (parsed_from->name) + strncpy(p->cid_name, parsed_from->name, sizeof(p->cid_name) - 1); + strncpy(p->cid_num, parsed_rpid->uri.username, sizeof(p->cid_num) - 1); ast_shrink_phone_number(p->cid_num); } - if (!ast_strlen_zero(user->cid_name) && !ast_strlen_zero(p->cid_num)) - strncpy(p->cid_name, user->cid_name, sizeof(p->cid_name) - 1); - strncpy(p->username, user->name, sizeof(p->username) - 1); - strncpy(p->peersecret, user->secret, sizeof(p->peersecret) - 1); - strncpy(p->peermd5secret, user->md5secret, sizeof(p->peermd5secret) - 1); - strncpy(p->accountcode, user->accountcode, sizeof(p->accountcode) -1); - strncpy(p->language, user->language, sizeof(p->language) -1); - strncpy(p->musicclass, user->musicclass, sizeof(p->musicclass) -1); - p->amaflags = user->amaflags; - p->callgroup = user->callgroup; - p->pickupgroup = user->pickupgroup; - p->callingpres = user->callingpres; - p->capability = user->capability; - p->jointcapability = user->capability; - if (p->peercapability) - p->jointcapability &= p->peercapability; - if (ast_test_flag(p, SIP_DTMF) == SIP_DTMF_RFC2833) - p->noncodeccapability |= AST_RTP_DTMF; - else - p->noncodeccapability &= ~AST_RTP_DTMF; - } - if (user && debug) - ast_verbose("Found user '%s'\n", user->name); - } else { - if (user) { - if (!mailbox && debug) - ast_verbose("Found user '%s', but fails host access\n", user->name); - ASTOBJ_UNREF(user,sip_destroy_user); + + if (p->rtp) { + ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); + ast_rtp_setnat(p->rtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); + } + if (p->vrtp) { + ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); + ast_rtp_setnat(p->vrtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); + } + if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, user->md5secret, cmd, uri, reliable, ignore))) { + sip_cancel_destroy(p); + ast_copy_flags(p, user, SIP_PROMISCREDIR | SIP_DTMF | SIP_REINVITE); + /* If we have a call limit, set flag */ + if (user->incominglimit) + ast_set_flag(p, SIP_CALL_LIMIT); + if (!ast_strlen_zero(user->context)) + strncpy(p->context, user->context, sizeof(p->context) - 1); + if (!ast_strlen_zero(user->cid_num) && !ast_strlen_zero(p->cid_num)) { + strncpy(p->cid_num, user->cid_num, sizeof(p->cid_num) - 1); + ast_shrink_phone_number(p->cid_num); + } + if (!ast_strlen_zero(user->cid_name) && !ast_strlen_zero(p->cid_num)) + strncpy(p->cid_name, user->cid_name, sizeof(p->cid_name) - 1); + strncpy(p->username, user->name, sizeof(p->username) - 1); + strncpy(p->peersecret, user->secret, sizeof(p->peersecret) - 1); + strncpy(p->peermd5secret, user->md5secret, sizeof(p->peermd5secret) - 1); + strncpy(p->accountcode, user->accountcode, sizeof(p->accountcode) -1); + strncpy(p->language, user->language, sizeof(p->language) -1); + strncpy(p->musicclass, user->musicclass, sizeof(p->musicclass) -1); + p->amaflags = user->amaflags; + p->callgroup = user->callgroup; + p->pickupgroup = user->pickupgroup; + p->callingpres = user->callingpres; + p->capability = user->capability; + p->jointcapability = user->capability; + if (p->peercapability) + p->jointcapability &= p->peercapability; + if (ast_test_flag(p, SIP_DTMF) == SIP_DTMF_RFC2833) + p->noncodeccapability |= AST_RTP_DTMF; + else + p->noncodeccapability &= ~AST_RTP_DTMF; + } + if (user && debug) + ast_verbose("Found user '%s'\n", user->name); + } else { + if (user) { + if (!mailbox && debug) + ast_verbose("Found user '%s', but fails host access\n", user->name); + ASTOBJ_UNREF(user,sip_destroy_user); + } + user = NULL; } - user = NULL; + } if (!user) { - /* If we didn't find a user match, check for peers */ + /* If we didn't get a user name, or find a user match, check for peers */ /* Look for peer based on the IP address we received data from */ - /* If peer is registred from this IP address or have this as a default - IP address, this call is from the peer + /* If peer is registered from this IP address or have this as a default + IP address, we assume this call is from the peer */ peer = find_peer(NULL, &p->recv, 1); if (peer) { @@ -5803,13 +5893,18 @@ ast_verbose("Found peer '%s'\n", peer->name); /* Take the peer */ ast_copy_flags(p, peer, SIP_TRUSTRPID | SIP_USECLIENTCODE | SIP_NAT | SIP_PROG_INBAND | SIP_OSPAUTH); - /* replace callerid if rpid found, and not restricted */ - if(!ast_strlen_zero(rpid_num) && ast_test_flag(p, SIP_TRUSTRPID)) { - if (*calleridname) - strncpy(p->cid_name, calleridname, sizeof(p->cid_name) - 1); - strncpy(p->cid_num, rpid_num, sizeof(p->cid_num) - 1); + + /* set cid_num, cid_name and callingpres from rpid if enabled */ + if (parsed_rpid && ast_test_flag(p, SIP_TRUSTRPID)) { + p->callingpres = get_rpid_privacy(parsed_rpid); + if (parsed_rpid->name) + strncpy(p->cid_name, parsed_rpid->name, sizeof(p->cid_name) - 1); + else if (parsed_from->name) + strncpy(p->cid_name, parsed_from->name, sizeof(p->cid_name) - 1); + strncpy(p->cid_num, parsed_rpid->uri.username, sizeof(p->cid_num) - 1); ast_shrink_phone_number(p->cid_num); } + if (p->rtp) { ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); ast_rtp_setnat(p->rtp, (ast_test_flag(p, SIP_NAT) & SIP_NAT_ROUTE)); @@ -5894,6 +5989,11 @@ if (user) ASTOBJ_UNREF(user,sip_destroy_user); + + if (parsed_rpid) + free(parsed_rpid); + free(parsed_from); + return res; }