Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 238563) +++ channels/chan_sip.c (working copy) @@ -1223,6 +1223,7 @@ \note in the future we could have multiple of these (per domain, per device group etc) */ static int global_match_auth_username; /*!< Match auth username if available instead of From: Default off. */ +static int global_match_callbackexten; /*!< Match peer callback extension: Default off. */ static int global_relaxdtmf; /*!< Relax DTMF */ static int global_prematuremediafilter; /*!< Enable/disable premature frames in a call (causing 183 early media) */ @@ -1973,6 +1974,7 @@ enum sip_peer_type { SIP_TYPE_PEER = (1 << 0), SIP_TYPE_USER = (1 << 1), + SIP_TYPE_SERVICE = (1 << 2) }; /*! \brief Structure for SIP peer data, we place calls to peers if registered or fixed IP address (host) @@ -1989,7 +1991,7 @@ AST_STRING_FIELD(username); /*!< Temporary username until registration */ AST_STRING_FIELD(accountcode); /*!< Account code */ AST_STRING_FIELD(tohost); /*!< If not dynamic, IP address */ - AST_STRING_FIELD(regexten); /*!< Extension to register (if regcontext is used) */ + AST_STRING_FIELD(regexten); /*!< Extension to register in dialplan (if regcontext is used) */ AST_STRING_FIELD(fromuser); /*!< From: user when calling this peer */ AST_STRING_FIELD(fromdomain); /*!< From: domain when calling this peer */ AST_STRING_FIELD(fullcontact); /*!< Contact registered with us (not in sip.conf) */ @@ -2003,6 +2005,7 @@ AST_STRING_FIELD(useragent); /*!< User agent in SIP request (saved from registration) */ AST_STRING_FIELD(mwi_from); /*!< Name to place in From header for outgoing NOTIFY requests */ AST_STRING_FIELD(engine); /*!< RTP Engine to use */ + AST_STRING_FIELD(callback); /*!< Holds callbackextension if specified */ AST_STRING_FIELD(unsolicited_mailbox); /*!< Mailbox to store received unsolicited MWI NOTIFY messages information in */ ); struct sip_socket socket; /*!< Socket used for this peer */ @@ -2020,6 +2023,11 @@ * for incoming calls */ unsigned short deprecated_username:1; /*!< If it's a realtime peer, are they using the deprecated "username" instead of "defaultuser" */ + /* Dunno if this belongs better in flags but they are full and dont want to start SIP_PAGE3 */ + unsigned short toheader_exten:1; /*!< Whether to get the extension from the to-header rather than the ruri of incoming invites */ + unsigned short register_callback:1; /*!< Whether to send a register request to the service provider for this peer */ + unsigned short callback_must_match:1; /*!< Whether to insist that the regcontact match the invite ruri */ + struct sip_auth *auth; /*!< Realm authentication list */ int amaflags; /*!< AMA Flags (for billing) */ int callingpres; /*!< Calling id presentation */ @@ -2040,6 +2048,7 @@ int maxcallbitrate; /*!< Maximum Bitrate for a video call */ int expire; /*!< When to expire this peer registration */ + int callbackexpiry; /*!< Configured expiry of callback registration */ format_t capability; /*!< Codec capability */ int rtptimeout; /*!< RTP timeout */ int rtpholdtimeout; /*!< RTP Hold Timeout */ @@ -2688,6 +2697,7 @@ static void change_redirecting_information(struct sip_pvt *p, struct sip_request *req, struct ast_party_redirecting *redirecting, int set_call_forward); static int get_domain(const char *str, char *domain, int len); static void get_realm(struct sip_pvt *p, const struct sip_request *req); +static int read_to_parts(struct sip_pvt *p, struct sip_request *req, char **name, char **number); /*-- TCP connection handling ---*/ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session); @@ -14095,6 +14105,12 @@ *colon = '\0'; uri = strsep(&uri, ";"); /* Remove userinfo options */ + + if (p->relatedpeer && (p->relatedpeer->toheader_exten)) { + /* Overwrite number from request uri with number from to-header uri */ + read_to_parts(p, req, NULL, &uri); + } + a = strsep(&a, ";"); /* Remove URI options */ ast_string_field_set(p, domain, a); @@ -14625,7 +14641,21 @@ return output; } +static int peer_match_callbackextension_cb(void *obj, void *arg, void *data, int flags) +{ + struct sip_peer *peer = arg; + struct sip_peer *peer2 = obj; + char *callback = data; + if ((peer->addr.sin_addr.s_addr == peer2->addr.sin_addr.s_addr) && + (peer->addr.sin_port == peer2->addr.sin_port) && + !(strcasecmp(callback, peer2->callback))) { + return CMP_MATCH | CMP_STOP; + } + return 0; +} + + /*! \brief Validate device authentication */ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, struct sip_request *req, int sipmethod, struct sockaddr_in *sin, @@ -14648,8 +14678,25 @@ /* Then find devices based on IP */ if (!peer) { peer = find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE, p->socket.type); + /* if peer is found by ip, and match_callbackextension is enabled, check + * to see if there is a tighter peer match by comparing the peer's callback*/ + struct sip_peer *peer2 = NULL; + if (peer && (peer->callback_must_match || global_match_callbackexten)) { + char *uri_tmp, *callback = NULL, *dummy, *domain; + uri_tmp = ast_strdupa(uri2); + parse_uri(uri_tmp, "sip:,sips:", &callback, &dummy, &domain, &dummy, &dummy, NULL); + if (!ast_strlen_zero(callback) && strcasecmp(callback, peer->callback)) { + if ((peer2 = ao2_t_callback_data(peers_by_ip, OBJ_POINTER, peer_match_callbackextension_cb, peer, callback, "ao2_find in peers_by_ip table, matching callback"))) { + unref_peer(peer, "check_peer_ok: unref_peer: new peer with better matching callback extension found, tossing this one"); + peer = peer2; } } + } + if (peer && peer->callback_must_match && !peer2) { + peer = NULL; + } + } + } if (!peer) { if (debug) @@ -18139,24 +18186,22 @@ *separator = '\0'; } + if (number) { + *number = ast_strdup(to_number); + } + /* We have the number. Let's get the name now. */ - if (*to_header == '\"') { - to_name = to_header + 1; - if (!(separator = (char *)find_closing_quote(to_name, NULL))) { - ast_log(LOG_NOTICE, "No closing quote in name section of To: header (%s)\n", to_header); - return -1; + if (name && (to_number > to_header)) { + separator = to_number - 1; + *separator = '\0'; + to_name = ast_strdup(to_header); + get_calleridname(to_header,to_name,sizeof(to_name)); + if (!ast_strlen_zero(to_name)) { + *name = to_name; } - *separator = '\0'; } - if (number) { - *number = ast_strdup(to_number); - } - if (name && !ast_strlen_zero(to_name)) { - *name = ast_strdup(to_name); - } - return 0; } @@ -20641,6 +20686,7 @@ const char *required; unsigned int required_profile = 0; struct ast_channel *c = NULL; /* New channel */ + struct sip_peer *authpeer =NULL; /* Matching Peer */ int reinvite = 0; int rtn; @@ -20960,7 +21006,7 @@ /* Handle authentication if this is our first invite */ struct ast_party_redirecting redirecting = {{0,},}; set_pvt_allowed_methods(p, req); - res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin); + res = check_user_full(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin, &authpeer); if (res == AUTH_CHALLENGE_SENT) { p->invitestate = INV_COMPLETED; /* Needs to restart in another INVITE transaction */ return 0; @@ -20979,6 +21025,9 @@ return 0; } + /* Successful authentication and peer matching so record the peer related to this pvt (for easy access to peer settings) */ + p->relatedpeer = ref_peer(authpeer, "setting dialog's relatedpeer pointer"); + /* If T38 is needed but not present, then make it magically appear */ if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && !p->udptl && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) { p->t38_maxdatagram = global_t38_maxdatagram; @@ -24712,6 +24761,7 @@ ast_string_field_set(peer, fromdomain, ""); ast_string_field_set(peer, fromuser, ""); ast_string_field_set(peer, regexten, ""); + ast_string_field_set(peer, callback, ""); peer->callgroup = 0; peer->pickupgroup = 0; peer->maxms = default_qualify; @@ -24789,7 +24839,6 @@ time_t regseconds = 0; struct ast_flags peerflags[2] = {{(0)}}; struct ast_flags mask[2] = {{(0)}}; - char callback[256] = ""; struct sip_peer tmp_peer; const char *srvlookup = NULL; static int deprecation_warning = 1; @@ -24894,6 +24943,8 @@ peer->type |= SIP_TYPE_USER; } else if (!strcasecmp(v->value, "friend")) { peer->type = SIP_TYPE_USER | SIP_TYPE_PEER; + } else if (!strcasecmp(v->value, "service")) { + peer->type = SIP_TYPE_SERVICE; } } else if (!strcasecmp(v->name, "remotesecret")) { ast_string_field_set(peer, remotesecret, v->value); @@ -25010,7 +25061,9 @@ } else if (!strcasecmp(v->name, "regexten")) { ast_string_field_set(peer, regexten, v->value); } else if (!strcasecmp(v->name, "callbackextension")) { - ast_copy_string(callback, v->value, sizeof(callback)); + ast_string_field_set(peer, callback, v->value); + } else if (!strcasecmp(v->name, "callbackexpiry")) { + peer->callbackexpiry = atoi(v->value); } else if (!strcasecmp(v->name, "amaflags")) { format = ast_cdr_amaflags2int(v->value); if (format < 0) { @@ -25154,6 +25207,12 @@ ast_string_field_set(peer, unsolicited_mailbox, v->value); } else if (!strcasecmp(v->name, "use_q850_reason")) { ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON); + } else if (!strcasecmp(v->name, "toheader_exten")) { + peer->toheader_exten = ast_true(v->value); + } else if (!strcasecmp(v->name, "register")) { + peer->register_callback = ast_true(v->value); + } else if (!strcasecmp(v->name, "callback_must_match")) { + peer->callback_must_match = ast_true(v->value); } } @@ -25210,6 +25269,14 @@ } } + /* Implement type=service as a peer type with callback registration, toheader extension and callback matching */ + if (peer->type & SIP_TYPE_SERVICE) { + peer->type = peer->type | SIP_TYPE_PEER; + peer->register_callback = 1; + peer->toheader_exten = 1; + peer->callback_must_match = 1; + } + if (!peer->default_outbound_transport) { /* Set default set of transports */ peer->transports = default_transports; @@ -25320,9 +25387,17 @@ peer->the_mark = 0; ast_free_ha(oldha); - if (!ast_strlen_zero(callback)) { /* build string from peer info */ + if (!ast_strlen_zero(peer->callback) || !(peer->callbackexpiry) || !(peer->register_callback)) { /* build string from peer info */ + char *reg_string; - if (asprintf(®_string, "%s?%s:%s@%s/%s", peer->name, peer->username, !ast_strlen_zero(peer->remotesecret) ? peer->remotesecret : peer->secret, peer->tohost, callback) < 0) { + if (asprintf(®_string, "%s?%s:%s@%s/%s~%d", + peer->name, + peer->username, + !ast_strlen_zero(peer->remotesecret) ? peer->remotesecret : peer->secret, + peer->tohost, + !ast_strlen_zero(peer->callback) ? peer->callback : peer->name, + !(peer->callbackexpiry) ? peer->callbackexpiry : default_expiry + ) < 0) { ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); } else if (reg_string) { sip_register(reg_string, 0); /* XXX TODO: count in registry_count */ @@ -25531,6 +25606,7 @@ sip_cfg.allowguest = DEFAULT_ALLOWGUEST; global_callcounter = DEFAULT_CALLCOUNTER; global_match_auth_username = FALSE; /*!< Match auth username if available instead of From: Default off. */ + global_match_callbackexten = FALSE; global_rtptimeout = 0; global_rtpholdtimeout = 0; global_rtpkeepalive = DEFAULT_RTPKEEPALIVE; @@ -25793,6 +25869,8 @@ sip_cfg.autocreatepeer = ast_true(v->value); } else if (!strcasecmp(v->name, "match_auth_username")) { global_match_auth_username = ast_true(v->value); + } else if (!strcasecmp(v->name, "match_callbackextension")) { + global_match_callbackexten = ast_true(v->value); } else if (!strcasecmp(v->name, "srvlookup")) { sip_cfg.srvlookup = ast_true(v->value); } else if (!strcasecmp(v->name, "pedantic")) {