SIP should use the transport type set in the Moved Temporarily for the next
authorTilghman Lesher <tilghman@meg.abyt.es>
Fri, 1 Aug 2008 16:39:51 +0000 (16:39 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Fri, 1 Aug 2008 16:39:51 +0000 (16:39 +0000)
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
configs/sip.conf.sample

index 9d2564d..7ca6233 100644 (file)
@@ -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);
index 0dcbced..034a5f5 100644 (file)
@@ -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
 ;
 ;