Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 212024) +++ channels/chan_sip.c (working copy) @@ -218,6 +218,7 @@ #include #include #include +#include /* needed for crc32 function */ #include "asterisk/network.h" #include "asterisk/paths.h" /* need ast_config_AST_SYSTEM_NAME */ @@ -1973,6 +1974,7 @@ AST_STRING_FIELD(mwi_from); /*!< Name to place in From header for outgoing NOTIFY requests */ AST_STRING_FIELD(engine); /*!< RTP Engine to use */ ); + char callback[256]; /*!< Callback Extension (writable to simplify find_peer2) */ struct sip_socket socket; /*!< Socket used for this peer */ enum sip_transport default_outbound_transport; /*!< Peer Registration may change the default outbound transport. If register expires, default should be reset. to this value */ @@ -2134,6 +2136,7 @@ /*! \brief The peer list: Users, Peers and Friends */ static struct ao2_container *peers; static struct ao2_container *peers_by_ip; +static struct ao2_container *peers_by_callback; /*! \brief The register list: Other SIP proxies we register with and place calls to */ static struct ast_register_list { @@ -2203,6 +2206,48 @@ } /*! + * \note the peer provides three fields combined to make a key: the sin_addr.s_addr, sin_port and callback fields. + */ +static int peer_callbackhash_cb(const void *obj, const int flags) +{ + const struct sip_peer *peer = obj; + int ret1 = peer->addr.sin_addr.s_addr; + if (ret1 < 0) + ret1 = -ret1; + + if (!ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT)) { + ret1 = ret1 + peer->addr.sin_port; + } + + if (!ast_strlen_zero(peer->callback)) { + ret1 = ret1 + (int) crc32(0L,(unsigned char*) peer->callback,sizeof(peer->callback)); + } + return ret1; +} + +/*! + * \note the peer provides three fields combined to make a key: the sin_addr.s_addr, sin_port and callback fields. + */ +static int peer_callbackcmp_cb(void *obj, void *arg, int flags) +{ + struct sip_peer *peer = obj, *peer2 = arg; + + if (peer->callback != peer2->callback) + return 0; + + if (peer->addr.sin_addr.s_addr != peer2->addr.sin_addr.s_addr) + return 0; + + if (!ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) && !ast_test_flag(&peer2->flags[0], SIP_INSECURE_PORT)) { + if (peer->addr.sin_port == peer2->addr.sin_port) + return CMP_MATCH | CMP_STOP; + else + return 0; + } + return CMP_MATCH | CMP_STOP; +} + +/*! * \note The only member of the dialog used here callid string */ static int dialog_hash_cb(const void *obj, const int flags) @@ -2578,6 +2623,7 @@ static struct sip_peer *temp_peer(const char *name); static void register_peer_exten(struct sip_peer *peer, int onoff); static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int forcenamematch, int devstate_only); +static struct sip_peer *find_peer2(const char *peer, char *callback, struct sockaddr_in *sin, int realtime, int forcenamematch, int devstate_only); static int sip_poke_peer_s(const void *data); static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_request *req); static void reg_source_db(struct sip_peer *peer); @@ -4840,7 +4886,8 @@ * \note Avoid using this function in new functions if there is a way to avoid it, * since it might cause a database lookup. */ -static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int which_objects, int devstate_only) + +static struct sip_peer *find_peer2(const char *peer, char *callback, struct sockaddr_in *sin, int realtime, int which_objects, int devstate_only) { struct sip_peer *p = NULL; struct sip_peer tmp_peer; @@ -4852,12 +4899,16 @@ tmp_peer.addr.sin_addr.s_addr = sin->sin_addr.s_addr; tmp_peer.addr.sin_port = sin->sin_port; tmp_peer.flags[0].flags = 0; - p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */ + ast_copy_string(tmp_peer.callback, callback, sizeof(tmp_peer.callback)); + p = ao2_t_find(peers_by_callback, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_callback table"); if (!p) { - ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT); - p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */ - if (p) { - return p; + p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */ + if (!p) { + ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT); + p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */ + if (p) { + return p; + } } } } @@ -4869,6 +4920,16 @@ return p; } + +static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime, int which_objects, int devstate_only) +{ + struct sip_peer *p = NULL; + find_peer2(peer, NULL, sin, realtime, which_objects, devstate_only); + return p; +} + + + /*! \brief Set nat mode on the various data sockets */ static void do_setnat(struct sip_pvt *p) { @@ -14009,7 +14070,31 @@ /* Then find devices based on IP */ if (!peer) { - peer = find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE); + /* use callback extension to distinguish multiple IP matches */ + char tmp[SIPBUFSIZE]; + char *dummy; + char *domain; + char *callback; + char *callback2; + ast_copy_string(tmp, uri2, sizeof(tmp)); + callback = tmp; + callback2 = ast_strdupa(callback); + if (p->socket.type == SIP_TRANSPORT_TLS) { + if (parse_uri(callback, "sips:", &callback, &dummy, &domain, &dummy, &dummy, NULL)) { + if (parse_uri(callback2, "sip:", &callback, &dummy, &domain, &dummy, &dummy, NULL)) { + ast_log(LOG_NOTICE, "Request line missing 'sip:', using it anyway\n"); + } + } + } else { + if (parse_uri(callback, "sip:", &callback, &dummy, &domain, &dummy, &dummy, NULL)) { + ast_log(LOG_NOTICE, "Request line missing 'sip:', using it anyway\n"); + } + } + if (sip_cfg.pedanticsipchecking) {ast_uri_decode(callback);} + ast_log(LOG_DEBUG, "Looking for peer with callback extension '%s' \n", callback); + /* found callback extension */ + + peer = find_peer2(NULL, callback, &p->recv, TRUE, FINDPEERS, FALSE); } /* If the peer is still not found, try the address and port from the @@ -24456,6 +24541,7 @@ sip_register(reg_string, 0); /* XXX TODO: count in registry_count */ ast_free(reg_string); } + ast_copy_string(peer->callback,callback,sizeof(peer->callback)); } return peer; } @@ -25945,6 +26031,7 @@ /* if the number of objects gets above MAX_XXX_BUCKETS, things will slow down */ peers = ao2_t_container_alloc(hash_peer_size, peer_hash_cb, peer_cmp_cb, "allocate peers"); peers_by_ip = ao2_t_container_alloc(hash_peer_size, peer_iphash_cb, peer_ipcmp_cb, "allocate peers_by_ip"); + peers_by_callback = ao2_t_container_alloc(hash_peer_size, peer_callbackhash_cb, peer_callbackcmp_cb, "allocate peers_by_callback"); dialogs = ao2_t_container_alloc(hash_dialog_size, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs"); ASTOBJ_CONTAINER_INIT(®l); /* Registry object list -- not searched for anything */ @@ -26136,6 +26223,7 @@ ao2_t_ref(peers, -1, "unref the peers table"); ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table"); + ao2_t_ref(peers_by_callback, -1, "unref the peers_by_callback table"); ao2_t_ref(dialogs, -1, "unref the dialogs table"); clear_sip_domains();