Resolve object matching issues related to the removal of the sip_user object.
authorRussell Bryant <russell@russellbryant.com>
Wed, 4 Mar 2009 21:01:05 +0000 (21:01 +0000)
committerRussell Bryant <russell@russellbryant.com>
Wed, 4 Mar 2009 21:01:05 +0000 (21:01 +0000)
Previously, chan_sip had both sip_peer and sip_user objects in memory.  A
patch went in to remove sip_user to simplify the code, since everything
could be done with just sip_peer.  This patch resolves some regressions
found that were introduced by those changes.

This code comes from svn/asterisk/team/group/sip-object-matching/.

Here is a list of the changes that have been made:

1) When doing a match by name with the find_peer() function, make it much
   easier to specify which objects should be matched by having a parameter
   that specifies exactly which object types should be considered.  Also,
   update find_by_name() to handle this parameter.  Finally, update all
   code to use the new option values.

2) When looking up an object for an outbound request by name, consider
   peers only.  (create_addr())

3) Only match peers on an incoming registration request.

4) When doing authentication (except for SUBSCRIBE), look up users
   by name, instead of all objects by name.

5) When doing authentication (except for SUBSCRIBE), after looking for
   a user by name, look for a peer by IP address, instead of all objects
   by IP address.

6) When handling the SIP qualify CLI command or manager action, look for
   a peer by name, instead of any object by name.

7) When handling the SIP unregister CLI command, look for a peer by name,
   instead of any object by name.

9) In sip_do_debug_peer(), search for a peer by name, instead of any object
   by name.

9) When handling the SIPPEER() dialplan function, search for a peer by name,
   instead of any object by name.

10) In the following session timer related functions, st_get_se(),
    st_get_refresher(), and st_get_mode(), when looking for an object for a
    given sip_pvt using pvt->peername, look for a peer by name, instead of any
    object by name.

11) Fix build_peer() to properly handle the case where separate type=peer and
    type=user entries were specified in sip.conf.

(closes issue #14505)
Reported by: lmadsen

Review: http://reviewboard.digium.com/r/172/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@180261 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c

index 6051fa9..dfde185 100644 (file)
@@ -487,8 +487,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #endif
 
 /* Arguments for find_peer */
-#define        FINDALLDEVICES  FALSE
-#define FINDONLYUSERS  TRUE
+#define FINDUSERS (1 << 0)
+#define FINDPEERS (1 << 1)
+#define FINDALLDEVICES (FINDUSERS | FINDPEERS)
 
 #define        SIPBUFSIZE              512             /*!< Buffer size for many operations */
 
@@ -1898,7 +1899,6 @@ struct sip_peer {
        
        /*XXX Seems like we suddenly have two flags with the same content. Why? To be continued... */
        enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
-       char onlymatchonip;             /*!< Only match on IP for incoming calls (old type=peer) */
 };
 
 
@@ -2438,7 +2438,7 @@ static void sip_dump_history(struct sip_pvt *dialog);
 
 /*--- Device object handling */
 static struct sip_peer *temp_peer(const char *name);
-static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int ispeer);
+static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime);
 static int update_call_counter(struct sip_pvt *fup, int event);
 static void sip_destroy_peer(struct sip_peer *peer);
 static void sip_destroy_peer_fn(void *peer);
@@ -4471,7 +4471,7 @@ static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_i
 
 
        /* Peer found in realtime, now build it in memory */
-       peer = build_peer(newpeername, var, varregs, TRUE, FALSE);
+       peer = build_peer(newpeername, var, varregs, TRUE);
        if (!peer) {
                if(peerlist)
                        ast_config_destroy(peerlist);
@@ -4513,38 +4513,52 @@ static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_i
 static int find_by_name(void *obj, void *arg, void *data, int flags)
 {
        struct sip_peer *search = obj, *match = arg;
-       int *forcenamematch = data;
+       int *which_objects = data;
 
        /* Usernames in SIP uri's are case sensitive. Domains are not */
        if (strcmp(search->name, match->name)) {
                return 0;
        }
 
-       /* If we're only looking for name matches, we should avoid type=peer devices,
-          since these should not match on any name-based search */
-       if (*forcenamematch && search->onlymatchonip) {
-               return 0;
+       switch (*which_objects) {
+       case FINDUSERS:
+               if (!(search->type & SIP_TYPE_USER)) {
+                       return 0;
+               }
+               break;
+       case FINDPEERS:
+               if (!(search->type & SIP_TYPE_PEER)) {
+                       return 0;
+               }
+               break;
+       case FINDALLDEVICES:
+               break;
        }
 
        return CMP_MATCH | CMP_STOP;
 }
 
-/*! \brief Locate device by name or ip address 
+/*! 
+ * \brief Locate device by name or ip address 
+ *
+ * \param which_objects Define which objects should be matched when doing a lookup
+ *        by name.  Valid options are FINDUSERS, FINDPEERS, or FINDALLDEVICES.
+ *        Note that this option is not used at all when doing a lookup by IP.
+ *
  *     This is used on find matching device on name or ip/port.
-       If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
-       
-       \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 forcenamematch, int devstate_only)
+ * If the device was declared as type=peer, we don't match on peer name on incoming INVITEs.
+ *
+ * \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)
 {
        struct sip_peer *p = NULL;
        struct sip_peer tmp_peer;
 
        if (peer) {
                ast_copy_string(tmp_peer.name, peer, sizeof(tmp_peer.name));
-               p = ao2_t_callback_data(peers, OBJ_POINTER, find_by_name, &tmp_peer, &forcenamematch, "ao2_find in peers table");
+               p = ao2_t_callback_data(peers, OBJ_POINTER, find_by_name, &tmp_peer, &which_objects, "ao2_find in peers table");
        } else if (sin) { /* search by addr? */
                tmp_peer.addr.sin_addr.s_addr = sin->sin_addr.s_addr;
                tmp_peer.addr.sin_port = sin->sin_port;
@@ -4839,7 +4853,7 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockadd
        dialog->sa.sin_family = AF_INET;
        dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
        dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
-       peer = find_peer(peername, NULL, TRUE, FINDALLDEVICES, FALSE);
+       peer = find_peer(peername, NULL, TRUE, FINDPEERS, FALSE);
 
        if (peer) {
                int res;
@@ -11972,7 +11986,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr
 
        ast_string_field_set(p, exten, name);
        build_contact(p);
-       peer = find_peer(name, NULL, TRUE, FINDALLDEVICES, FALSE);
+       peer = find_peer(name, NULL, TRUE, FINDPEERS, FALSE);
        if (!(peer && ast_apply_ha(peer->ha, sin))) {
                /* Peer fails ACL check */
                if (peer) {
@@ -12884,11 +12898,11 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
                peer = find_peer(of, NULL, TRUE, FINDALLDEVICES, FALSE);
        } else {
                /* First find devices based on username (avoid all type=peer's) */
-               peer = find_peer(of, NULL, TRUE, FINDONLYUSERS, FALSE);
+               peer = find_peer(of, NULL, TRUE, FINDUSERS, FALSE);
 
                /* Then find devices based on IP */
                if (!peer) {
-                       peer = find_peer(NULL, &p->recv, TRUE, FINDALLDEVICES, FALSE);
+                       peer = find_peer(NULL, &p->recv, TRUE, FINDPEERS, FALSE);
                }
        }
 
@@ -14226,7 +14240,7 @@ static char *_sip_qualify_peer(int type, int fd, struct mansession *s, const str
                return CLI_SHOWUSAGE;
 
        load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? TRUE : FALSE;
-       if ((peer = find_peer(argv[3], NULL, load_realtime, FINDALLDEVICES, FALSE))) {
+       if ((peer = find_peer(argv[3], NULL, load_realtime, FINDPEERS, FALSE))) {
                sip_poke_peer(peer, 1);
                unref_peer(peer, "qualify: done with peer");
        } else if (type == 0) {
@@ -14308,7 +14322,8 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                return CLI_SHOWUSAGE;
 
        load_realtime = (argc == 5 && !strcmp(argv[4], "load")) ? TRUE : FALSE;
-       peer = find_peer(argv[3], NULL, load_realtime, FINDALLDEVICES, FALSE);
+       peer = find_peer(argv[3], NULL, load_realtime, FINDPEERS, FALSE);
+
        if (s) {        /* Manager */
                if (peer) {
                        const char *id = astman_get_header(m, "ActionID");
@@ -14590,7 +14605,7 @@ static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args
        /* Load from realtime storage? */
        load_realtime = (a->argc == 5 && !strcmp(a->argv[4], "load")) ? TRUE : FALSE;
 
-       if ((user = find_peer(a->argv[3], NULL, load_realtime, TRUE, FALSE))) {
+       if ((user = find_peer(a->argv[3], NULL, load_realtime, FINDUSERS, FALSE))) {
                ao2_lock(user);
                ast_cli(a->fd, "\n\n");
                ast_cli(a->fd, "  * Name       : %s\n", user->name);
@@ -14748,7 +14763,7 @@ static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
        if (a->argc != 3)
                return CLI_SHOWUSAGE;
        
-       if ((peer = find_peer(a->argv[2], NULL, load_realtime, FINDALLDEVICES, TRUE))) {
+       if ((peer = find_peer(a->argv[2], NULL, load_realtime, FINDPEERS, TRUE))) {
                if (peer->expire > 0) {
                        expire_register(ref_peer(peer, "ref for expire_register"));
                        ast_cli(a->fd, "Unregistered peer \'%s\'\n\n", a->argv[2]);
@@ -15758,7 +15773,7 @@ static char *sip_do_debug_ip(int fd, char *arg)
 /*! \brief  Turn on SIP debugging for a given peer */
 static char *sip_do_debug_peer(int fd, char *arg)
 {
-       struct sip_peer *peer = find_peer(arg, NULL, TRUE, FINDALLDEVICES, FALSE);
+       struct sip_peer *peer = find_peer(arg, NULL, TRUE, FINDPEERS, FALSE);
        if (!peer)
                ast_cli(fd, "No such peer '%s'\n", arg);
        else if (peer->addr.sin_addr.s_addr == 0)
@@ -16213,7 +16228,7 @@ static int function_sippeer(struct ast_channel *chan, const char *cmd, char *dat
        else
                colname = "ip";
 
-       if (!(peer = find_peer(data, NULL, TRUE, FINDALLDEVICES, FALSE)))
+       if (!(peer = find_peer(data, NULL, TRUE, FINDPEERS, FALSE)))
                return -1;
 
        if (!strcasecmp(colname, "ip")) {
@@ -21571,7 +21586,7 @@ int st_get_se(struct sip_pvt *p, int max)
                if (p->stimer->st_cached_max_se) {
                        return p->stimer->st_cached_max_se;
                } else if (p->peername) {
-                       struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDALLDEVICES, FALSE);
+                       struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDPEERS, FALSE);
                        if (pp) {
                                p->stimer->st_cached_max_se = pp->stimer.st_max_se;
                                unref_peer(pp, "unref peer pointer from find_peer call in st_get_se");
@@ -21584,7 +21599,7 @@ int st_get_se(struct sip_pvt *p, int max)
                if (p->stimer->st_cached_min_se) {
                        return p->stimer->st_cached_min_se;
                } else if (p->peername) {
-                       struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDALLDEVICES, FALSE);
+                       struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDPEERS, FALSE);
                        if (pp) {
                                p->stimer->st_cached_min_se = pp->stimer.st_min_se;
                                unref_peer(pp, "unref peer pointer from find_peer call in st_get_se (2)");
@@ -21606,7 +21621,7 @@ enum st_refresher st_get_refresher(struct sip_pvt *p)
                return p->stimer->st_cached_ref;
 
        if (p->peername) {
-               struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDALLDEVICES, FALSE);
+               struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDPEERS, FALSE);
                if (pp) {
                        p->stimer->st_cached_ref = pp->stimer.st_ref;
                        unref_peer(pp, "unref peer pointer from find_peer call in st_get_refresher");
@@ -21631,7 +21646,7 @@ enum st_mode st_get_mode(struct sip_pvt *p)
                return p->stimer->st_cached_mode;
 
        if (p->peername) {
-               struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDALLDEVICES, FALSE);
+               struct sip_peer *pp = find_peer(p->peername, NULL, TRUE, FINDPEERS, FALSE);
                if (pp) {
                        p->stimer->st_cached_mode = pp->stimer.st_mode_oper;
                        unref_peer(pp, "unref peer pointer from find_peer call in st_get_mode");
@@ -22473,7 +22488,7 @@ static void add_peer_mailboxes(struct sip_peer *peer, const char *value)
 }
 
 /*! \brief Build peer from configuration (file or realtime static/dynamic) */
-static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime, int ispeer)
+static struct sip_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime)
 {
        struct sip_peer *peer = NULL;
        struct ast_ha *oldha = NULL;
@@ -22488,7 +22503,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
        const char *srvlookup = NULL;
        static int deprecation_warning = 1;
        struct ast_str *fullcontact = ast_str_alloca(512);
-       
+
        if (!realtime || ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
                /* Note we do NOT use find_peer here, to avoid realtime recursion */
                /* We also use a case-sensitive comparison (unlike find_peer) so
@@ -22498,13 +22513,13 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                ast_copy_string(tmp_peer.name, name, sizeof(tmp_peer.name));
                peer = ao2_t_find(peers, &tmp_peer, OBJ_POINTER | OBJ_UNLINK, "find and unlink peer from peers table");
        }
-       
+
        if (peer) {
                /* Already in the list, remove it and it will be added back (or FREE'd)  */
                found++;
                if (!(peer->the_mark))
                        firstpass = 0;
-       } else {
+       } else {
                if (!(peer = ao2_t_alloc(sizeof(*peer), sip_destroy_peer_fn, "allocate a peer struct")))
                        return NULL;
 
@@ -22520,17 +22535,16 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                        ast_atomic_fetchadd_int(&speerobjs, 1);
        }
 
-       peer->onlymatchonip = ispeer;           /* If type=peer, don't match on caller ID for incoming calls */
-
        /* Note that our peer HAS had its reference count increased */
        if (firstpass) {
                peer->lastmsgssent = -1;
                oldha = peer->ha;
                peer->ha = NULL;
                set_peer_defaults(peer);        /* Set peer defaults */
+               peer->type = 0;
        }
        if (!found && name)
-                       ast_copy_string(peer->name, name, sizeof(peer->name));
+               ast_copy_string(peer->name, name, sizeof(peer->name));
 
        /* If we have channel variables, remove them (reload) */
        if (peer->chanvars) {
@@ -22584,13 +22598,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                        }
                } else if (!strcasecmp(v->name, "type")) {
                        if (!strcasecmp(v->value, "peer")) {
-                               peer->onlymatchonip = TRUE;             /* For realtime support, add type=peer in the table */
-                               peer->type = SIP_TYPE_PEER;
+                               peer->type |= SIP_TYPE_PEER;
                        } else if (!strcasecmp(v->value, "user")) {
-                               peer->onlymatchonip = FALSE;
-                               peer->type = SIP_TYPE_USER;
+                               peer->type |= SIP_TYPE_USER;
                        } else if (!strcasecmp(v->value, "friend")) {
-                               peer->onlymatchonip = FALSE;
                                peer->type = SIP_TYPE_USER | SIP_TYPE_PEER;
                        }
                } else if (!strcasecmp(v->name, "remotesecret")) {
@@ -23667,10 +23678,10 @@ static int reload_config(enum channelreloadreason reason)
                                hassip = ast_variable_retrieve(ucfg, cat, "hassip");
                                registersip = ast_variable_retrieve(ucfg, cat, "registersip");
                                if (ast_true(hassip) || (!hassip && genhassip)) {
-                                       peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0, FALSE);
+                                       peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
                                        if (peer) {
                                                ao2_t_link(peers, peer, "link peer into peer table");
-                                               if (peer->addr.sin_addr.s_addr) {
+                                               if ((peer->type & SIP_TYPE_PEER) && peer->addr.sin_addr.s_addr) {
                                                        ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
                                                }
                                                
@@ -23719,27 +23730,24 @@ static int reload_config(enum channelreloadreason reason)
                        ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
                        continue;
                } else {
-                       int is_peer = 0;
                        if (!strcasecmp(utype, "user")) {
-                               is_peer = 1;
+                               ;
                        } else if (!strcasecmp(utype, "friend")) {
-                               is_peer = 1;
-                       } else if (!strcasecmp(utype, "peer"))
-                               is_peer = 2;
-                       else {
+                               ;
+                       } else if (!strcasecmp(utype, "peer")) {
+                               ;
+                       } else {
                                ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf");
                                continue;
                        }
-                       if (is_peer) {
-                               peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0, is_peer == 2);
-                               if (peer) {
-                                       ao2_t_link(peers, peer, "link peer into peers table");
-                                       if (peer->addr.sin_addr.s_addr) {
-                                               ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
-                                       }
-                                       unref_peer(peer, "unref the result of the build_peer call. Now, the links from the tables are the only ones left.");
-                                       peer_count++;
+                       peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
+                       if (peer) {
+                               ao2_t_link(peers, peer, "link peer into peers table");
+                               if ((peer->type & SIP_TYPE_PEER) && peer->addr.sin_addr.s_addr) {
+                                       ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
                                }
+                               unref_peer(peer, "unref the result of the build_peer call. Now, the links from the tables are the only ones left.");
+                               peer_count++;
                        }
                }
        }