From 6cb65834759097e0e65c2453e92005a2e77d754f Mon Sep 17 00:00:00 2001 From: Tilghman Lesher Date: Fri, 1 Aug 2008 16:39:51 +0000 Subject: SIP should use the transport type set in the Moved Temporarily for the next invite. (closes issue #11843) Reported by: pestermann Patches: 20080723__issue11843_302_ignores_transport_16branch.diff uploaded by bbryant (license 36) 20080723__issue11843_302_ignores_transport_trunk.diff uploaded by bbryant (license 36) Tested by: pabelanger git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@135126 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_sip.c | 174 ++++++++++++++++++++++++++++++++++-------------- configs/sip.conf.sample | 2 +- 2 files changed, 124 insertions(+), 52 deletions(-) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 9d2564d99..7ca623332 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1950,6 +1950,37 @@ static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer static int sip_refer_allocate(struct sip_pvt *p); static void ast_quiet_chan(struct ast_channel *chan); static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); +/*! + * \brief generic function for determining if a correct transport is being + * used to contact a peer + * + * this is done as a macro so that the "tmpl" var can be passed either a + * sip_request or a sip_peer + */ +#define check_request_transport(peer, tmpl) ({ \ + int ret = 0; \ + if (peer->socket.type == tmpl->socket.type) \ + ; \ + else if (!(peer->transports & tmpl->socket.type)) {\ + ast_log(LOG_ERROR, \ + "'%s' is not a valid transport for '%s'. we only use '%s'! ending call.\n", \ + get_transport(tmpl->socket.type), peer->name, get_transport_list(peer) \ + ); \ + ret = 1; \ + } else if (peer->socket.type & SIP_TRANSPORT_TLS) { \ + ast_log(LOG_WARNING, \ + "peer '%s' HAS NOT USED (OR SWITCHED TO) TLS in favor of '%s' (but this was allowed in sip.conf)!\n", \ + peer->name, get_transport(tmpl->socket.type) \ + ); \ + } else { \ + ast_debug(1, \ + "peer '%s' has contacted us over %s even though we prefer %s.\n", \ + peer->name, get_transport(tmpl->socket.type), get_transport(peer->socket.type) \ + ); \ + }\ + (ret); \ +}) + /*--- Device monitoring and Device/extension state/event handling */ static int cb_extensionstate(char *context, char* exten, int state, void *data); @@ -2103,7 +2134,7 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p); static void build_via(struct sip_pvt *p); static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer); -static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockaddr_in *sin); +static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockaddr_in *sin, int newdialog); static char *generate_random_string(char *buf, size_t size); static void build_callid_pvt(struct sip_pvt *pvt); static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain); @@ -4135,6 +4166,11 @@ static void copy_socket_data(struct sip_socket *to_sock, const struct sip_socket */ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) { + /* this checks that the dialog is contacting the peer on a valid + * transport type based on the peers transport configuration, + * otherwise, this function bails out */ + if (dialog->socket.type && check_request_transport(peer, dialog)) + return -1; copy_socket_data(&dialog->socket, &peer->socket); if ((peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr) && @@ -4260,10 +4296,11 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) return 0; } + /*! \brief create address structure from device name * Or, if peer not found, find it in the global DNS * returns TRUE (-1) on failure, FALSE on success */ -static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockaddr_in *sin) +static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockaddr_in *sin, int newdialog) { struct hostent *hp; struct ast_hostent ahp; @@ -4284,13 +4321,12 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockadd peer = find_peer(peername, NULL, TRUE, TRUE); if (peer) { - int res = create_addr_from_peer(dialog, peer); + int res; + if (newdialog) + dialog->socket.type = 0; + res = create_addr_from_peer(dialog, peer); unref_peer(peer, "create_addr: unref peer from find_peer hashtab lookup"); return res; - } else { - /* Setup default parameters for this dialog's socket. Currently we only support regular UDP SIP as the default */ - dialog->socket.type = SIP_TRANSPORT_UDP; - dialog->socket.port = bindaddr.sin_port; } ast_string_field_set(dialog, tohost, peername); @@ -4306,7 +4342,10 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockadd if (sin) { memcpy(&dialog->sa.sin_addr, &sin->sin_addr, sizeof(dialog->sa.sin_addr)); if (!sin->sin_port) { - portno = port ? atoi(port) : (dialog->socket.type & SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT; + if (ast_strlen_zero(port) || sscanf(port, "%u", &portno) != 1) { + portno = dialog->socket.type & SIP_TRANSPORT_TLS ? + STANDARD_TLS_PORT : STANDARD_SIP_PORT; + } } else { portno = ntohs(sin->sin_port); } @@ -4339,6 +4378,8 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockadd memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr)); } + if (!dialog->socket.type) + dialog->socket.type = SIP_TRANSPORT_UDP; dialog->sa.sin_port = htons(portno); dialog->recv = dialog->sa; return 0; @@ -4375,7 +4416,7 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) struct sip_pvt *p = ast->tech_pvt; /* chan is locked, so the reference cannot go away */ struct varshead *headp; struct ast_var_t *current; - const char *referer = NULL; /* SIP referrer */ + const char *referer = NULL; /* SIP referrer */ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name); @@ -4406,9 +4447,8 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) p->t38.state = T38_LOCAL_DIRECT; ast_debug(1, "T38State change to %d on channel %s\n", p->t38.state, ast->name); } - } - + res = 0; ast_set_flag(&p->flags[0], SIP_OUTGOING); @@ -9442,7 +9482,7 @@ static int manager_sipnotify(struct mansession *s, const struct message *m) return -1; } - if (create_addr(p, channame, NULL)) { + if (create_addr(p, channame, NULL, 0)) { /* Maybe they're not registered, etc. */ dialog_unlink_all(p, TRUE, TRUE); dialog_unref(p, "unref dialog inside for loop" ); @@ -9654,7 +9694,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char * r->us.sin_port = htons(r->portno); /* Find address to hostname */ - if (create_addr(p, r->hostname, &r->us)) { + if (create_addr(p, r->hostname, &r->us, 0)) { /* we have what we hope is a temporary network error, * probably DNS. We need to reschedule a registration try */ dialog_unlink_all(p, TRUE, TRUE); @@ -10852,27 +10892,10 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr break; } - if (peer->socket.type != req->socket.type ) { - if (!(peer->transports & req->socket.type)) { - ast_log(LOG_ERROR, - "peer '%s' has contacted us over %s, but we only accept '%s' for this peer! ending call.\n", - peer->name, get_transport(req->socket.type), get_transport_list(peer) - ); - - ast_set_flag(&p->flags[0], SIP_PENDINGBYE); - transmit_response_with_date(p, "403 Forbidden", req); - res = AUTH_BAD_TRANSPORT; - } else if (peer->socket.type & SIP_TRANSPORT_TLS) { - ast_log(LOG_WARNING, - "peer '%s' HAS STOPPED USING TLS in favor of '%s' (but this was allowed in sip.conf)!\n", - peer->name, get_transport(req->socket.type) - ); - } else { - ast_log(LOG_DEBUG, - "peer '%s' has contacted us over %s even though we prefer %s.\n", - peer->name, get_transport(req->socket.type), get_transport(peer->socket.type) - ); - } + if (check_request_transport(peer, req)) { + ast_set_flag(&p->flags[0], SIP_PENDINGBYE); + transmit_response_with_date(p, "403 Forbidden", req); + res = AUTH_BAD_TRANSPORT; } } } @@ -14423,7 +14446,7 @@ static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_arg return CLI_FAILURE; } - if (create_addr(p, a->argv[i], NULL)) { + if (create_addr(p, a->argv[i], NULL, 0)) { /* Maybe they're not registered, etc. */ dialog_unlink_all(p, TRUE, TRUE); dialog_unref(p, "unref dialog inside for loop" ); @@ -14996,13 +15019,41 @@ static struct ast_custom_function sipchaninfo_function = { static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) { char tmp[SIPBUFSIZE]; - char *s, *e, *t; + char *s, *e, *t, *trans; char *domain; + enum sip_transport transport = SIP_TRANSPORT_UDP; ast_copy_string(tmp, get_header(req, "Contact"), sizeof(tmp)); if ((t = strchr(tmp, ','))) *t = '\0'; - s = remove_uri_parameters(get_in_brackets(tmp)); + + s = get_in_brackets(tmp); + if ((trans = strcasestr(s, ";transport="))) do { + trans += 11; + + if ((e = strchr(trans, ';'))) + *e = '\0'; + + if (!strncasecmp(trans, "tcp", 3)) + transport = SIP_TRANSPORT_TCP; + else if (!strncasecmp(trans, "tls", 3)) + transport = SIP_TRANSPORT_TLS; + else { + if (strncasecmp(trans, "udp", 3)) + ast_debug(1, "received contact with an invalid transport, '%s'\n", s); + transport = SIP_TRANSPORT_UDP; + } + } while(0); + s = remove_uri_parameters(s); + + if (p->socket.ser) { + ao2_ref(p->socket.ser, -1); + p->socket.ser = NULL; + } + + p->socket.fd = -1; + p->socket.type = transport; + if (ast_test_flag(&p->flags[0], SIP_PROMISCREDIR)) { if (!strncasecmp(s, "sip:", 4)) s += 4; @@ -15011,9 +15062,9 @@ static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req) e = strchr(s, '/'); if (e) *e = '\0'; - ast_debug(2, "Found promiscuous redirection to 'SIP/%s'\n", s); + ast_debug(2, "Found promiscuous redirection to 'SIP/::::%s@%s'\n", get_transport(transport), s); if (p->owner) - ast_string_field_build(p->owner, call_forward, "SIP/%s", s); + ast_string_field_build(p->owner, call_forward, "SIP/::::%s@%s", get_transport(transport), s); } else { e = strchr(tmp, '@'); if (e) { @@ -17095,7 +17146,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int if ((peerorhost = strchr(uri, ':'))) { *peerorhost++ = '\0'; } - create_addr(p, peerorhost, NULL); + create_addr(p, peerorhost, NULL, 0); ast_string_field_set(p, theirtag, NULL); for (pkt = p->packets; pkt; pkt = pkt->next) { if (pkt->seqno == p->icseq && pkt->method == SIP_INVITE) { @@ -20068,6 +20119,8 @@ static struct ast_channel *sip_request_call(const char *type, int format, void * char *secret = NULL; char *md5secret = NULL; char *authname = NULL; + char *trans = NULL; + enum sip_transport transport = 0; int oldformat = format; /* mask request with some set of allowed formats. @@ -20119,29 +20172,48 @@ static struct ast_channel *sip_request_call(const char *type, int format, void * *host++ = '\0'; ext = tmp; secret = strchr(ext, ':'); - if (secret) { - *secret++ = '\0'; - md5secret = strchr(secret, ':'); - if (md5secret) { - *md5secret++ = '\0'; - authname = strchr(md5secret, ':'); - if (authname) - *authname++ = '\0'; - } + } + if (secret) { + *secret++ = '\0'; + md5secret = strchr(secret, ':'); + } + if (md5secret) { + *md5secret++ = '\0'; + authname = strchr(md5secret, ':'); + } + if (authname) { + *authname++ = '\0'; + trans = strchr(authname, ':'); + } + if (trans) { + *trans++ = '\0'; + if (!strcasecmp(trans, "tcp")) + transport = SIP_TRANSPORT_TCP; + else if (!strcasecmp(trans, "tls")) + transport = SIP_TRANSPORT_TLS; + else { + if (strcasecmp(trans, "udp")) + ast_log(LOG_WARNING, "'%s' is not a valid transport option to Dial() for SIP calls, using udp by default.\n", trans); + transport = SIP_TRANSPORT_UDP; } - } else { + } + + if (!host) { ext = strchr(tmp, '/'); if (ext) *ext++ = '\0'; host = tmp; } + p->socket.fd = -1; + p->socket.type = transport; + /* We now have host = peer name, DNS host name or DNS domain (for SRV) ext = extension (user part of URI) dnid = destination of the call (applies to the To: header) */ - if (create_addr(p, host, NULL)) { + if (create_addr(p, host, NULL, 1)) { *cause = AST_CAUSE_UNREGISTERED; ast_debug(3, "Cant create SIP call - target device not registred\n"); dialog_unlink_all(p, TRUE, TRUE); diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 0dcbced76..034a5f597 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -7,7 +7,7 @@ ; syntaxes for dialing SIP devices. ; SIP/devicename ; SIP/username@domain (SIP uri) -; SIP/username[:password[:md5secret[:authname]]]@host[:port] +; SIP/username[:password[:md5secret[:authname[:transport]]]]@host[:port] ; SIP/devicename/extension ; ; -- cgit v1.2.3