Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:56 2007

Asterisk developer's documentation :: Codename Pineapple


sip3_objects.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  * and Edvina AB, Sollentuna, Sweden (chan_sip3 changes/additions)
00006  *
00007  * Mark Spencer <markster@digium.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*!
00021  * \file
00022  * \brief Various SIP object handling functions
00023  * (phones, trunks, services)
00024  * Version 3 of chan_sip
00025  *
00026  * \author Mark Spencer <markster@digium.com>
00027  * \author Olle E. Johansson <oej@edvina.net> (all the chan_sip3 changes)
00028  *
00029  * See Also:
00030  * \arg \ref AstCREDITS
00031  *
00032  */
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 53128 $")
00037 
00038 #include <stdio.h>
00039 #include <ctype.h>
00040 #include <string.h>
00041 #include <unistd.h>
00042 #include <sys/socket.h>
00043 #include <sys/ioctl.h>
00044 #include <net/if.h>
00045 #include <errno.h>
00046 #include <stdlib.h>
00047 #include <fcntl.h>
00048 #include <netdb.h>
00049 #include <signal.h>
00050 #include <sys/signal.h>
00051 #include <netinet/in.h>
00052 #include <netinet/in_systm.h>
00053 #include <arpa/inet.h>
00054 #include <netinet/ip.h>
00055 #include <regex.h>
00056 
00057 #include "asterisk/lock.h"
00058 #include "asterisk/channel.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/logger.h"
00061 #include "asterisk/module.h"
00062 #include "asterisk/pbx.h"
00063 #include "asterisk/options.h"
00064 #include "asterisk/sched.h"
00065 #include "asterisk/io.h"
00066 #include "asterisk/rtp.h"
00067 #include "asterisk/udptl.h"
00068 #include "asterisk/acl.h"
00069 #include "asterisk/manager.h"
00070 #include "asterisk/callerid.h"
00071 #include "asterisk/cli.h"
00072 #include "asterisk/app.h"
00073 #include "asterisk/musiconhold.h"
00074 #include "asterisk/dsp.h"
00075 #include "asterisk/features.h"
00076 #include "asterisk/srv.h"
00077 #include "asterisk/astdb.h"
00078 #include "asterisk/causes.h"
00079 #include "asterisk/utils.h"
00080 #include "asterisk/file.h"
00081 #include "asterisk/astobj.h"
00082 #include "asterisk/dnsmgr.h"
00083 #include "asterisk/devicestate.h"
00084 #include "asterisk/linkedlists.h"
00085 #include "asterisk/stringfields.h"
00086 #include "asterisk/monitor.h"
00087 #include "asterisk/localtime.h"
00088 #include "asterisk/abstract_jb.h"
00089 #include "asterisk/compiler.h"
00090 #include "sip3.h"
00091 #include "sip3funcs.h"
00092 
00093 
00094 struct sip_device_list devicelist;           /*!< The device list */
00095 
00096 /*! \brief Support routine for find_device */
00097 static int sip_addrcmp(char *name, struct sockaddr_in *sin)
00098 {
00099    /* We know name is the first field, so we can cast */
00100    struct sip_device *peer = (struct sip_device *) name;
00101    return   !(!inaddrcmp(&peer->addr, sin) || 
00102                (ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) &&
00103                (peer->addr.sin_addr.s_addr == sin->sin_addr.s_addr)));
00104 }
00105 
00106 
00107 /*! \brief Update peer object in realtime storage 
00108    If the Asterisk system name is set in asterisk.conf, we will use
00109    that name and store that in the "regserver" field in the sippeers
00110    table to facilitate multi-server setups.
00111 */
00112 GNURK void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, int expiry)
00113 {
00114    char port[10];
00115    char ipaddr[INET_ADDRSTRLEN];
00116    char regseconds[20];
00117 
00118    char *sysname = ast_config_AST_SYSTEM_NAME;
00119    char *syslabel = NULL;
00120 
00121    time_t nowtime = time(NULL) + expiry;
00122    const char *fc = fullcontact ? "fullcontact" : NULL;
00123    
00124    snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);  /* Expiration time */
00125    ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
00126    snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
00127    
00128    if (ast_strlen_zero(sysname)) /* No system name, disable this */
00129       sysname = NULL;
00130    else if (ast_test_flag(&global.flags[1], SIP_PAGE2_RTSAVE_SYSNAME))
00131       syslabel = "regserver";
00132 
00133    if (fc)
00134       ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
00135          "port", port, "regseconds", regseconds,
00136          "defaultuser", username, fc, fullcontact, syslabel, sysname, NULL); /* note fc and syslabel _can_ be NULL */
00137    else
00138       ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
00139          "port", port, "regseconds", regseconds,
00140          "defaultuser", username, syslabel, sysname, NULL); /* note syslabel _can_ be NULL */
00141 }
00142 
00143 /*! \brief Automatically add peer extension to dial plan */
00144 GNURK void register_peer_exten(struct sip_device *device, int onoff)
00145 {
00146    char multi[256];
00147    char *stringp, *ext, *context;
00148 
00149    /* XXX note that global.regcontext is both a global 'enable' flag and
00150     * the name of the global regexten context, if not specified
00151     * individually.
00152     */
00153    if (ast_strlen_zero(global.regcontext))
00154       return;
00155 
00156    ast_copy_string(multi, S_OR(device->extra.regexten, device->name), sizeof(multi));
00157    stringp = multi;
00158    while ((ext = strsep(&stringp, "&"))) {
00159       if ((context = strchr(ext, '@'))) {
00160          *context++ = '\0';   /* split ext@context */
00161          if (!ast_context_find(context)) {
00162             ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context);
00163             continue;
00164          }
00165       } else {
00166          context = global.regcontext;
00167       }
00168       if (onoff)
00169          ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
00170              ast_strdup(device->name), ast_free, "SIP");
00171       else
00172          ast_context_remove_extension(context, ext, 1, NULL);
00173    }
00174 }
00175 
00176 /*! \brief Destroy device object from memory */
00177 GNURK void sip_destroy_device(struct sip_device *device)
00178 {
00179    logdebug(3, "Destroying SIP device %s\n", device->name);
00180    //if (option_debug > 2)
00181       //ast_log(LOG_DEBUG, "Destroying SIP %s %s\n", device->type & SIP_USER ? "user" : "peer", device->name);
00182 
00183    /* Delete it, it needs to disappear */
00184    if (device->call)
00185       sip_destroy(device->call);
00186    if (device->chanvars) {
00187       ast_variables_destroy(device->chanvars);
00188       device->chanvars = NULL;
00189    }
00190 
00191    if (device->mailbox.mwipvt)      /* We have an active subscription, delete it */
00192       sip_destroy(device->mailbox.mwipvt);
00193 
00194    if (device->expire > -1)
00195       ast_sched_del(sched, device->expire);
00196    if (device->pokeexpire > -1)
00197       ast_sched_del(sched, device->pokeexpire);
00198    ast_free_ha(device->ha);
00199 
00200    if (device->type & SIP_PEER) {
00201       register_peer_exten(device, FALSE);
00202       clear_realm_authentication(device->auth);
00203       device->auth = (struct sip_auth *) NULL;
00204       if (ast_test_flag((&device->flags[1]), SIP_PAGE2_SELFDESTRUCT))
00205          sipcounters.autocreated_peers--;
00206       else if (ast_test_flag(&device->flags[0], SIP_REALTIME))
00207          sipcounters.realtime_peers--;
00208       else
00209          sipcounters.static_peers--;
00210    } 
00211    if (device->type & SIP_USER) {   /* SIP_USER */
00212       if (ast_test_flag(&device->flags[0], SIP_REALTIME))
00213          sipcounters.realtime_users--;
00214       else
00215          sipcounters.static_users--;
00216    }
00217    if (device->dnsmgr)
00218       ast_dnsmgr_release(device->dnsmgr);
00219    if (device->registry) {
00220       device->registry->peer = NULL;
00221       ASTOBJ_UNREF(device->registry,sip_registry_destroy);
00222    }
00223 
00224    /* Free the stringfield pool */
00225    ast_string_field_free_pools(device);
00226    free(device);
00227 }
00228 
00229 /*! \brief Update peer data in database (if used) */
00230 GNURK void update_peer(struct sip_device *device, int expiry)
00231 {
00232    int rtcachefriends = ast_test_flag(&device->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
00233    if (ast_test_flag(&global.flags[1], SIP_PAGE2_RTUPDATE) &&
00234        (ast_test_flag(&device->flags[0], SIP_REALTIME) || rtcachefriends)) {
00235       realtime_update_peer(device->name, &device->addr, device->defaultuser, rtcachefriends ? device->fullcontact : NULL, expiry);
00236    }
00237 }
00238 
00239 /*! \brief Locate peer by name or ip address 
00240  * This is used on incoming SIP message to find matching peer on ip
00241    or outgoing message to find matching peer on name */
00242 GNURK struct sip_device *find_device(const char *device, struct sockaddr_in *sin, int realtime)
00243 {
00244    struct sip_device *peer = NULL;
00245 
00246    if (device)
00247       peer = ASTOBJ_CONTAINER_FIND(&devicelist, device);
00248    else
00249       peer = ASTOBJ_CONTAINER_FIND_FULL(&devicelist, sin, name, sip_addr_hashfunc, 1, sip_addrcmp);
00250 
00251    if (!peer && realtime)
00252       peer = realtime_peer(device, sin);
00253 
00254    return peer;
00255 }
00256 
00257 /*! \brief Create temporary peer (used in autocreatepeer mode) */
00258 GNURK struct sip_device *temp_device(const char *name)
00259 {
00260    struct sip_device *peer;
00261 
00262    if (!(peer = ast_calloc(1, sizeof(*peer))))
00263       return NULL;
00264 
00265    sipcounters.autocreated_peers++;
00266    ASTOBJ_INIT(peer);
00267    peer->type = SIP_PEER;
00268    set_device_defaults(peer);
00269 
00270    ast_copy_string(peer->name, name, sizeof(peer->name));
00271 
00272    ast_set_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT);
00273    ast_set_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC);
00274    peer->prefs = global.default_prefs;
00275    reg_source_db(peer);
00276 
00277    return peer;
00278 }
00279 
00280 /*! \brief Get registration details from Asterisk DB 
00281    \ref chan_sip3_registrydb
00282 */
00283 GNURK void reg_source_db(struct sip_device *peer)
00284 {
00285    /*! \page chan_sip3_registrydb SIP3 :: THe registry database (astdb)
00286       The SIP3 registry database contains a string that contains
00287       fields separated by | characters. When a device registers,
00288       the string is stored to the database in order to allow
00289       restarts of Asterisk without loosing data.
00290       When Asterisk restarts, the SIP channel loads data from 
00291       the ASTDB "sip3-registry" family in order to populate
00292       the peer list with registered peers.
00293 
00294       \b Fields:
00295 
00296       - \b Expirytime: The time (unix time) when this registration expires
00297       - \b IP address: Registered IP address or NAT address
00298       - \b Port: Registered port our NAT port
00299       - \b Expiry: How long this registration is valid
00300       - \b Contact: The Contact header registered with Asterisk
00301 
00302       \b Functions:
00303    
00304       - \ref reg_source_db()
00305 
00306       A problem with this is that if Asterisk has not been running for more
00307       than 30 secs, we might not be able to keep NAT relations alive and will
00308       send out keepalives that will be refused by the NAT. The device will
00309       quickly become UNREACHABLE until we get a new registration from the inside.
00310    */
00311 
00312    char data[BUFSIZ * 4];
00313    struct in_addr in;
00314    int expiry;
00315    int port;
00316    time_t exptime;
00317    char *scan, *expirytime, *addr, *port_str, *expiry_str, *contact;
00318 
00319    if (ast_test_flag(&peer->flags[1], SIP_PAGE2_RT_FROMCONTACT))  /*! \bug XXX What is this???? */
00320       return;
00321    if (ast_db_get("SIP3-Registry", peer->name, data, sizeof(data)))
00322       return;
00323 
00324    scan = data;
00325    expirytime = strsep(&scan, "|");
00326    addr = strsep(&scan, "|");
00327    port_str = strsep(&scan, "|");
00328    expiry_str = strsep(&scan, "|");
00329    contact = scan;   /* Contact include sip: and has to be the last part of the database entry as long as we use : as a separator */
00330 
00331    exptime = (time_t) atoi(expirytime);
00332    if (exptime < time(NULL)) {
00333       /* This peer as expired, registration no longer valid */
00334       if (option_debug > 1 && sipdebug)
00335          ast_log(LOG_DEBUG, "Peer %s has expired. Deleting entry in astdb\n", peer->name);
00336       ast_db_del("SIP3-Registry", peer->name);
00337       return;
00338    }
00339    if (!inet_aton(addr, &in))
00340       return;
00341 
00342    if (port_str)
00343       port = atoi(port_str);
00344    else
00345       return;
00346 
00347    if (expiry_str)
00348       expiry = atoi(expiry_str);
00349    else
00350       return;
00351 
00352    if (contact)
00353       ast_string_field_set(peer, fullcontact, contact);
00354 
00355    if (option_verbose > 2)
00356       ast_verbose(VERBOSE_PREFIX_3 "SIP Loaded device from astdb: '%s' at %s:%d for %d\n",
00357              peer->name, ast_inet_ntoa(in), port, expiry);
00358 
00359    memset(&peer->addr, 0, sizeof(peer->addr));
00360    peer->addr.sin_family = AF_INET;
00361    peer->addr.sin_addr = in;
00362    peer->addr.sin_port = htons(port);
00363 
00364    /* Schedule a poke only, pretty soon */
00365    if (peer->pokeexpire > -1)
00366       ast_sched_del(sched, peer->pokeexpire);
00367    peer->pokeexpire = ast_sched_add(sched, ast_random() % 5000 + 1, sip_poke_peer_s, peer);
00368 
00369    if (peer->expire > -1)
00370       ast_sched_del(sched, peer->expire);
00371    
00372 
00373    //peer->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, peer);
00374    peer->expire = ast_sched_add(sched, (exptime + 10 - time(NULL)) * 1000, expire_register, peer);
00375 
00376    register_peer_exten(peer, TRUE);
00377 }
00378 
00379 /*! \brief Remove registration data from realtime database or AST/DB when registration expires */
00380 GNURK void destroy_association(struct sip_device *device)
00381 {
00382    if (!ast_test_flag(&global.flags[1], SIP_PAGE2_IGNOREREGEXPIRE)) {
00383       if (ast_test_flag(&device->flags[1], SIP_PAGE2_RT_FROMCONTACT))
00384          ast_update_realtime("sippeers", "name", device->name, "fullcontact", "", "ipaddr", "", "port", "", "regseconds", "0", "defaultuser", "", "regserver", "", NULL);
00385       else 
00386          ast_db_del("SIP3-Registry", device->name);
00387    }
00388 }
00389 
00390 /*! \brief Expire registration of SIP device */
00391 GNURK int expire_register(void *data)
00392 {
00393    struct sip_device *device = data;
00394    
00395    if (!device)      /* Hmmm. We have no peer. Weird. */
00396       return 0;
00397 
00398    memset(&device->addr, 0, sizeof(device->addr));
00399 
00400    destroy_association(device);  /* remove registration data from storage */
00401    
00402    manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", device->name);
00403    register_peer_exten(device, FALSE); /* Remove regexten */
00404    device->expire = -1;
00405    ast_device_state_changed("SIP/%s", device->name);
00406 
00407    /* Do we need to release this peer from memory? 
00408       Only for realtime peers and autocreated peers
00409    */
00410    if (ast_test_flag(&device->flags[1], SIP_PAGE2_SELFDESTRUCT) ||
00411        ast_test_flag(&device->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
00412       device = ASTOBJ_CONTAINER_UNLINK(&devicelist, device);   /* Remove from peer list */
00413       device_unref(device);
00414    }
00415 
00416    return 0;
00417 }

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen