diff --git a/channels/chan_sip.c b/channels/chan_sip.c index a0031cb0d9..8e385ffce5 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1510,6 +1510,7 @@ enum sip_peer_type { struct sip_peer { char name[80]; /*!< peer->name is the unique name of this object */ struct sip_socket socket; /*!< Socket used for this peer */ + enum sip_transport default_outbound_transport; /*!< Peer Registration may change the default outbound transport. */ unsigned int transports:3; /*!< Transports (enum sip_transport) that are acceptable for this peer */ char secret[80]; /*!< Password */ char md5secret[80]; /*!< Password in MD5 */ @@ -1543,7 +1544,7 @@ struct sip_peer { int lastmsgssent; unsigned int sipoptions; /*!< Supported SIP options */ struct ast_flags flags[2]; /*!< SIP_ flags */ - + /*! Mailboxes that this peer cares about */ AST_LIST_HEAD_NOLOCK(, sip_mailbox) mailboxes; @@ -1565,7 +1566,7 @@ struct sip_peer { struct ast_dnsmgr_entry *dnsmgr;/*!< DNS refresh manager for peer */ struct sockaddr_in addr; /*!< IP address of peer */ int maxcallbitrate; /*!< Maximum Bitrate for a video call */ - + /* Qualification */ struct sip_pvt *call; /*!< Call pointer */ int pokeexpire; /*!< When to expire poke (qualify= checking) */ @@ -2819,6 +2820,27 @@ static inline int sip_debug_test_pvt(struct sip_pvt *p) return 0; return sip_debug_test_addr(sip_real_dst(p)); } + /*! \brief Return int representing a bit field of transport types found in const char *transport */ + static int get_transport_str2enum(const char *transport) + { + int res = 0; + + if (ast_strlen_zero(transport)) { + return res; + } + + if (!strcasecmp(transport, "udp")) { + res |= SIP_TRANSPORT_UDP; + } + if (!strcasecmp(transport, "tcp")) { + res |= SIP_TRANSPORT_TCP; + } + if (!strcasecmp(transport, "tls")) { + res |= SIP_TRANSPORT_TLS; + } + + return res; +} static inline const char *get_transport_list(struct sip_peer *peer) { switch (peer->transports) { @@ -3628,7 +3650,7 @@ static char *get_in_brackets(char *tmp) * \endverbatim */ static int parse_uri(char *uri, char *scheme, - char **ret_name, char **pass, char **domain, char **port, char **options) + char **ret_name, char **pass, char **domain, char **port, char **options, char **transport) { char *name = NULL; int error = 0; @@ -3647,6 +3669,17 @@ static int parse_uri(char *uri, char *scheme, error = -1; } } + if (transport) { + char *t, *type = ""; + *transport = ""; + if ((t = strstr(uri, "transport="))) { + strsep(&t, "="); + if ((type = strsep(&t, ";"))) { + *transport = type; + } + } + } + if (!domain) { /* if we don't want to split around domain, keep everything as a name, * so we need to do nothing here, except remember why. @@ -10399,11 +10432,24 @@ static void destroy_association(struct sip_peer *peer) } } +static void set_peer_transport(struct sip_peer *peer, int transport) +{ + /* if the transport type changes, clear all socket data */ + if (peer->socket.type != transport) { + peer->socket.type = transport; + peer->socket.fd = -1; + if (peer->socket.tcptls_session) { + ao2_ref(peer->socket.tcptls_session, -1); + peer->socket.tcptls_session = NULL; + } + } +} + /*! \brief Expire registration of SIP peer */ static int expire_register(const void *data) { struct sip_peer *peer = (struct sip_peer *)data; - + if (!peer) /* Hmmm. We have no peer. Weird. */ return 0; @@ -10411,7 +10457,8 @@ static int expire_register(const void *data) memset(&peer->addr, 0, sizeof(peer->addr)); destroy_association(peer); /* remove registration data from storage */ - + set_peer_transport(peer, peer->default_outbound_transport); + manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name); register_peer_exten(peer, FALSE); /* Remove regexten */ ast_devstate_changed(AST_DEVICE_UNKNOWN, "SIP/%s", peer->name); @@ -10550,13 +10597,13 @@ static int __set_address_from_contact(const char *fullcontact, struct sockaddr_i /* We have only the part in here so we just need to parse a SIP URI.*/ if (tcp) { - if (parse_uri(contact, "sips:", &contact, NULL, &host, &pt, NULL)) { - if (parse_uri(contact2, "sip:", &contact, NULL, &host, &pt, NULL)) + if (parse_uri(contact, "sips:", &contact, NULL, &host, &pt, NULL, NULL)) { + if (parse_uri(contact2, "sip:", &contact, NULL, &host, &pt, NULL, NULL)) ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact); } port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_TLS_PORT; } else { - if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL)) + if (parse_uri(contact, "sip:", &contact, NULL, &host, &pt, NULL, NULL)) ast_log(LOG_NOTICE, "'%s' is not a valid SIP contact (missing sip:) trying to use anyway\n", contact); port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT; } @@ -10588,21 +10635,22 @@ static int set_address_from_contact(struct sip_pvt *pvt) return __set_address_from_contact(pvt->fullcontact, &pvt->sa, pvt->socket.type == SIP_TRANSPORT_TLS ? 1 : 0); } - /*! \brief Parse contact header and save registration (peer registration) */ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *peer, struct sip_request *req) { - char contact[SIPBUFSIZE]; + char contact[SIPBUFSIZE]; char data[SIPBUFSIZE]; const char *expires = get_header(req, "Expires"); int expire = atoi(expires); - char *curi, *host, *pt, *curi2; + char *curi, *host, *pt, *curi2, *transport; int port; + int transport_type; const char *useragent; struct hostent *hp; struct ast_hostent ahp; struct sockaddr_in oldsin, testsin; + ast_copy_string(contact, get_header(req, "Contact"), sizeof(contact)); if (ast_strlen_zero(expires)) { /* No expires header, try look in Contact: */ @@ -10617,8 +10665,6 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st } } - if (peer->socket.type == req->socket.type) - copy_socket_data(&peer->socket, &req->socket); copy_socket_data(&pvt->socket, &req->socket); /* Look for brackets */ @@ -10640,12 +10686,13 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st } else if (!strcasecmp(curi, "*") || !expire) { /* Unregister this peer */ /* This means remove all registrations and return OK */ memset(&peer->addr, 0, sizeof(peer->addr)); + set_peer_transport(peer, peer->default_outbound_transport); AST_SCHED_DEL_UNREF(sched, peer->expire, unref_peer(peer, "remove register expire ref")); destroy_association(peer); - + register_peer_exten(peer, FALSE); /* Remove extension from regexten= setting in sip.conf */ peer->fullcontact[0] = '\0'; peer->useragent[0] = '\0'; @@ -10667,17 +10714,36 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st /* Make sure it's a SIP URL */ if (pvt->socket.type == SIP_TRANSPORT_TLS) { - if (parse_uri(curi, "sips:", &curi, NULL, &host, &pt, NULL)) { - if (parse_uri(curi2, "sip:", &curi, NULL, &host, &pt, NULL)) + if (parse_uri(curi, "sips:", &curi, NULL, &host, &pt, NULL, &transport)) { + if (parse_uri(curi2, "sip:", &curi, NULL, &host, &pt, NULL, &transport)) ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n"); } port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_TLS_PORT; } else { - if (parse_uri(curi, "sip:", &curi, NULL, &host, &pt, NULL)) + if (parse_uri(curi, "sip:", &curi, NULL, &host, &pt, NULL, &transport)) ast_log(LOG_NOTICE, "Not a valid SIP contact (missing sip:) trying to use anyway\n"); port = !ast_strlen_zero(pt) ? atoi(pt) : STANDARD_SIP_PORT; } + /* handle the transport type specified in Contact header. */ + if ((transport_type = get_transport_str2enum(transport))) { + /* if the port is not specified but the transport is, make sure to set the + * default port to match the specified transport. This may or may not be the + * same transport used by the pvt struct for the Register dialog. */ + if (ast_strlen_zero(pt)) { + port = (transport_type == SIP_TRANSPORT_TLS) ? STANDARD_TLS_PORT : STANDARD_SIP_PORT; + } + } else { + transport_type = pvt->socket.type; + } + + /* if the peer's socket type is different than the Registration + * transport type, change it. If it got this far, it is a + * supported type, but check just in case */ + if ((peer->socket.type != transport_type) && (peer->transports & transport_type)) { + set_peer_transport(peer, transport_type); + } + oldsin = peer->addr; /* If we were already linked into the peers_by_ip container unlink ourselves so nobody can find us */ @@ -10713,6 +10779,16 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st peer->addr = pvt->recv; } + /* if the Contact header information copied into peer->addr matches the + * received address, and the transport types are the same, then copy socket + * data into the peer struct */ + if ((peer->socket.type == pvt->socket.type) && + (peer->addr.sin_addr.s_addr == pvt->recv.sin_addr.s_addr) && + (peer->addr.sin_port == pvt->recv.sin_port)){ + + copy_socket_data(&peer->socket, &pvt->socket); + } + /* Now that our address has been updated put ourselves back into the container for lookups */ ao2_t_link(peers_by_ip, peer, "ao2_link into peers_by_ip table"); @@ -10732,7 +10808,7 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st if (peer->is_realtime && !ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) { peer->expire = -1; } else { - peer->expire = ast_sched_add(sched, (expire + 10) * 1000, expire_register, + peer->expire = ast_sched_add(sched, (expire + 10) * 1000, expire_register, ref_peer(peer, "add registration ref")); if (peer->expire == -1) { unref_peer(peer, "remote registration ref"); @@ -10744,7 +10820,7 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st XXX WHY???? XXX \todo check this */ - if (!peer->rt_fromcontact && (peer->socket.type & SIP_TRANSPORT_UDP)) + if (!peer->rt_fromcontact && (peer->socket.type & SIP_TRANSPORT_UDP)) ast_db_put("SIP/Registry", peer->name, data); manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\nPort: %d\r\n", peer->name, ast_inet_ntoa(peer->addr.sin_addr), ntohs(peer->addr.sin_port)); @@ -12439,12 +12515,12 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ /* ignore all fields but name */ if (p->socket.type == SIP_TRANSPORT_TLS) { - if (parse_uri(of, "sips:", &of, &dummy, &domain, &dummy, &dummy)) { - if (parse_uri(of2, "sip:", &of, &dummy, &domain, &dummy, &dummy)) + if (parse_uri(of, "sips:", &of, &dummy, &domain, &dummy, &dummy, NULL)) { + if (parse_uri(of2, "sip:", &of, &dummy, &domain, &dummy, &dummy, NULL)) ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); } } else { - if (parse_uri(of, "sip:", &of, &dummy, &domain, &dummy, &dummy)) + if (parse_uri(of, "sip:", &of, &dummy, &domain, &dummy, &dummy, NULL)) ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); } @@ -21611,9 +21687,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str { struct sip_peer *peer = NULL; struct ast_ha *oldha = NULL; - int found=0; - int firstpass=1; - int format=0; /* Ama flags */ + int found = 0; + int firstpass = 1; + int format = 0; /* Ama flags */ time_t regseconds = 0; struct ast_flags peerflags[2] = {{(0)}}; struct ast_flags mask[2] = {{(0)}}; @@ -21671,8 +21747,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str /* If we have realm authentication information, remove them (reload) */ clear_realm_authentication(peer->auth); peer->auth = NULL; + peer->default_outbound_transport = 0; peer->transports = 0; - peer->socket.type = 0; for (; v || ((v = alt) && !(alt=NULL)); v = v->next) { if (handle_common_options(&peerflags[0], &mask[0], v)) @@ -21684,7 +21760,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str while ((trans = strsep(&val, ","))) { trans = ast_skip_blanks(trans); - if (!strncasecmp(trans, "udp", 3)) + if (!strncasecmp(trans, "udp", 3)) peer->transports |= SIP_TRANSPORT_UDP; else if (!strncasecmp(trans, "tcp", 3)) peer->transports |= SIP_TRANSPORT_TCP; @@ -21693,9 +21769,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str else ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, udp will be used.\n", trans); - if (!peer->socket.type) { /*!< The first transport listed should be used for outgoing */ - peer->socket.type = peer->transports; - peer->socket.fd = -1; + if (!peer->default_outbound_transport) { /*!< The first transport listed should be default outbound */ + peer->default_outbound_transport = peer->transports; } } } else if (realtime && !strcasecmp(v->name, "regseconds")) { @@ -21996,6 +22071,16 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str peer->socket.type = SIP_TRANSPORT_UDP; } + /* The default transport type set during build_peer should only replace the socket.type when... + * 1. Registration is not present and the socket.type and default transport types are different. + * 2. The socket.type is not an acceptable transport type after rebuilding peer. + * 3. The socket.type is not set yet. */ + if (((peer->socket.type != peer->default_outbound_transport) && (peer->expire == -1)) || + !(peer->socket.type & peer->transports) || !(peer->socket.type)) { + + set_peer_transport(peer, peer->default_outbound_transport); + } + if (fullcontact->used > 0) { ast_copy_string(peer->fullcontact, fullcontact->str, sizeof(peer->fullcontact)); peer->rt_fromcontact = TRUE; @@ -22018,7 +22103,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str if ((params = strchr(_srvlookup, ';'))) { *params++ = '\0'; } - + snprintf(transport, sizeof(transport), "_sip._%s", get_transport(peer->socket.type)); if (ast_dnsmgr_lookup(_srvlookup, &peer->addr, &peer->dnsmgr, global_srvlookup ? transport : NULL)) { diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 433606c1b4..ec3fafa6de 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -819,11 +819,14 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;secret=guessit ;defaultuser=yourusername ; Authentication user for outbound proxies ;fromuser=yourusername ; Many SIP providers require this! -;fromdomain=provider.sip.domain +;fromdomain=provider.sip.domain ;host=box.provider.com -;transport=udp,tcp ; This sets the transport type to udp for outgoing, and will -; ; accept both tcp and udp. Default is udp. The first transport -; ; listed will always be used for outgoing connections. +;transport=udp,tcp ; This sets the default transport type to udp for outgoing, and will +; ; accept both tcp and udp. The default transport type is only used for +; ; outbound messages until a Registration takes place. During the +; ; peer Registration the transport type may change to another supported +; ; type if the peer requests so. + ;usereqphone=yes ; This provider requires ";user=phone" on URI ;callcounter=yes ; Enable call counter ;busylevel=2 ; Signal busy at 2 or more calls