Wed Apr 6 11:29:47 2011

Asterisk developer's documentation


res_stun_monitor.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2010, Digium, Inc.
00005  *
00006  * David Vossel <dvossel@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \brief STUN Network Monitor
00022  *
00023  * \author David Vossel <dvossel@digium.com>
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 291192 $")
00029 
00030 #include "asterisk/module.h"
00031 #include "asterisk/event.h"
00032 #include "asterisk/sched.h"
00033 #include "asterisk/config.h"
00034 #include "asterisk/stun.h"
00035 #include "asterisk/netsock2.h"
00036 #include "asterisk/lock.h"
00037 #include <fcntl.h>
00038 
00039 static const int DEFAULT_MONITOR_REFRESH = 30;
00040 
00041 static const char stun_conf_file[] = "res_stun_monitor.conf";
00042 static struct ast_sched_thread *sched;
00043 
00044 static struct {
00045    struct sockaddr_in stunaddr;      /*!< The stun address we send requests to*/
00046    struct sockaddr_in externaladdr;  /*!< current perceived external address. */
00047    ast_mutex_t lock;
00048    unsigned int refresh;
00049    int stunsock;
00050    unsigned int monitor_enabled:1;
00051    unsigned int externaladdr_known:1;
00052 } args;
00053 
00054 static inline void stun_close_sock(void)
00055 {
00056    if (args.stunsock != -1) {
00057       close(args.stunsock);
00058       args.stunsock = -1;
00059       memset(&args.externaladdr, 0, sizeof(args.externaladdr));
00060       args.externaladdr_known = 0;
00061    }
00062 }
00063 
00064 /* \brief purge the stun socket's receive buffer before issuing a new request
00065  *
00066  * XXX Note that this is somewhat of a hack.  This function is essentially doing
00067  * a cleanup on the socket rec buffer to handle removing any STUN responses we have not
00068  * handled.  This is called before sending out a new STUN request so we don't read
00069  * a latent previous response thinking it is new.
00070  */
00071 static void stun_purge_socket(void)
00072 {
00073    int flags = fcntl(args.stunsock, F_GETFL);
00074    int res = 0;
00075    unsigned char reply_buf[1024];
00076 
00077    fcntl(args.stunsock, F_SETFL, flags | O_NONBLOCK);
00078    while (res != -1) {
00079       /* throw away everything in the buffer until we reach the end. */
00080       res = recv(args.stunsock, reply_buf, sizeof(reply_buf), 0);
00081    }
00082    fcntl(args.stunsock, F_SETFL, flags & ~O_NONBLOCK);
00083 }
00084 
00085 /* \brief called by scheduler to send STUN request */
00086 static int stun_monitor_request(const void *blarg)
00087 {
00088    int res;
00089    int generate_event = 0;
00090    struct sockaddr_in answer = { 0, };
00091 
00092 
00093    /* once the stun socket goes away, this scheduler item will go away as well */
00094    ast_mutex_lock(&args.lock);
00095    if (args.stunsock == -1) {
00096       ast_log(LOG_ERROR, "STUN monitor: can not send STUN request, socket is not open\n");
00097       goto monitor_request_cleanup;
00098    }
00099 
00100    stun_purge_socket();
00101 
00102    if (!(ast_stun_request(args.stunsock, &args.stunaddr, NULL, &answer)) &&
00103       (memcmp(&args.externaladdr, &answer, sizeof(args.externaladdr)))) {
00104       const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
00105       int newport = ntohs(answer.sin_port);
00106 
00107       ast_log(LOG_NOTICE, "STUN MONITOR: Old external address/port %s:%d now seen as %s:%d \n",
00108          ast_inet_ntoa(args.externaladdr.sin_addr), ntohs(args.externaladdr.sin_port),
00109          newaddr, newport);
00110 
00111       memcpy(&args.externaladdr, &answer, sizeof(args.externaladdr));
00112 
00113       if (args.externaladdr_known) {
00114          /* the external address was already known, and has changed... generate event. */
00115          generate_event = 1;
00116 
00117       } else {
00118          /* this was the first external address we found, do not alert listeners
00119           * until this address changes to something else. */
00120          args.externaladdr_known = 1;
00121       }
00122    }
00123 
00124    if (generate_event) {
00125       struct ast_event *event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
00126       if (!event) {
00127          ast_log(LOG_ERROR, "STUN monitor: could not create AST_EVENT_NETWORK_CHANGE event.\n");
00128          goto monitor_request_cleanup;
00129       }
00130       if (ast_event_queue(event)) {
00131          ast_event_destroy(event);
00132          event = NULL;
00133          ast_log(LOG_ERROR, "STUN monitor: could not queue AST_EVENT_NETWORK_CHANGE event.\n");
00134          goto monitor_request_cleanup;
00135       }
00136    }
00137 
00138 monitor_request_cleanup:
00139    /* always refresh this scheduler item.  It will be removed elsewhere when
00140     * it is supposed to go away */
00141    res = args.refresh * 1000;
00142    ast_mutex_unlock(&args.lock);
00143 
00144    return res;
00145 }
00146 
00147 /* \brief stops the stun monitor thread
00148  * \note do not hold the args->lock while calling this
00149  */
00150 static void stun_stop_monitor(void)
00151 {
00152    if (sched) {
00153       sched = ast_sched_thread_destroy(sched);
00154       ast_log(LOG_NOTICE, "STUN monitor stopped\n");
00155    }
00156    /* it is only safe to destroy the socket without holding arg->lock
00157     * after the sched thread is destroyed */
00158    stun_close_sock();
00159 }
00160 
00161 /* \brief starts the stun monitor thread
00162  * \note The args->lock MUST be held when calling this function
00163  */
00164 static int stun_start_monitor(void)
00165 {
00166    struct ast_sockaddr dst;
00167    /* clean up any previous open socket */
00168    stun_close_sock();
00169 
00170    /* create destination ast_sockaddr */
00171    ast_sockaddr_from_sin(&dst, &args.stunaddr);
00172 
00173    /* open new socket binding */
00174    args.stunsock = socket(AF_INET, SOCK_DGRAM, 0);
00175    if (args.stunsock < 0) {
00176       ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
00177       return -1;
00178    }
00179 
00180    if (ast_connect(args.stunsock, &dst) != 0) {
00181       ast_log(LOG_WARNING, "SIP STUN Failed to connect to %s\n", ast_sockaddr_stringify(&dst));
00182       stun_close_sock();
00183       return -1;
00184    }
00185 
00186    /* if scheduler thread is not started, make sure to start it now */
00187    if (sched) {
00188       return 0; /* already started */
00189    }
00190 
00191    if (!(sched = ast_sched_thread_create())) {
00192       ast_log(LOG_ERROR, "Failed to create stun monitor scheduler thread\n");
00193       stun_close_sock();
00194       return -1;
00195    }
00196 
00197    if (ast_sched_thread_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
00198       ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
00199       sched = ast_sched_thread_destroy(sched);
00200       stun_close_sock();
00201       return -1;
00202    }
00203 
00204    ast_log(LOG_NOTICE, "STUN monitor started\n");
00205    return 0;
00206 }
00207 
00208 static int load_config(int startup)
00209 {
00210    struct ast_flags config_flags = { 0, };
00211    struct ast_config *cfg;
00212    struct ast_variable *v;
00213 
00214    if (!startup) {
00215       ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
00216    }
00217 
00218    if (!(cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags)) ||
00219       cfg == CONFIG_STATUS_FILEINVALID) {
00220       ast_log(LOG_ERROR, "Unable to load config %s\n", stun_conf_file);
00221       return -1;
00222    }
00223 
00224    if (cfg == CONFIG_STATUS_FILEUNCHANGED && !startup) {
00225       return 0;
00226    }
00227 
00228    /* set defaults */
00229    args.monitor_enabled = 0;
00230    memset(&args.stunaddr, 0, sizeof(args.stunaddr));
00231    args.refresh = DEFAULT_MONITOR_REFRESH;
00232 
00233    for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00234       if (!strcasecmp(v->name, "stunaddr")) {
00235          args.stunaddr.sin_port = htons(STANDARD_STUN_PORT);
00236          if (ast_parse_arg(v->value, PARSE_INADDR, &args.stunaddr)) {
00237             ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value);
00238          } else {
00239             ast_log(LOG_NOTICE, "STUN monitor enabled: %s\n", v->value);
00240             args.monitor_enabled = 1;
00241          }
00242       } else if (!strcasecmp(v->name, "stunrefresh")) {
00243          if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
00244             ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
00245             args.refresh = DEFAULT_MONITOR_REFRESH;
00246          } else {
00247             ast_log(LOG_NOTICE, "STUN Monitor set to refresh every %d seconds\n", args.refresh);
00248          }
00249       } else {
00250          ast_log(LOG_WARNING, "SIP STUN: invalid config option %s at line %d\n", v->value, v->lineno);
00251       }
00252    }
00253 
00254    ast_config_destroy(cfg);
00255 
00256    return 0;
00257 }
00258 
00259 static int __reload(int startup)
00260 {
00261    int res;
00262 
00263    ast_mutex_lock(&args.lock);
00264    if (!(res = load_config(startup)) && args.monitor_enabled) {
00265       res = stun_start_monitor();
00266    }
00267    ast_mutex_unlock(&args.lock);
00268 
00269    if ((res == -1) || !args.monitor_enabled) {
00270       args.monitor_enabled = 0;
00271       stun_stop_monitor();
00272    }
00273 
00274    return res;
00275 }
00276 
00277 static int reload(void)
00278 {
00279    return __reload(0);
00280 }
00281 
00282 static int unload_module(void)
00283 {
00284    stun_stop_monitor();
00285    ast_mutex_destroy(&args.lock);
00286    return 0;
00287 }
00288 
00289 static int load_module(void)
00290 {
00291    ast_mutex_init(&args.lock);
00292    args.stunsock = -1;
00293    memset(&args.externaladdr, 0, sizeof(args.externaladdr));
00294    args.externaladdr_known = 0;
00295    sched = NULL;
00296    if (__reload(1)) {
00297       stun_stop_monitor();
00298       ast_mutex_destroy(&args.lock);
00299       return AST_MODULE_LOAD_DECLINE;
00300    }
00301 
00302    return AST_MODULE_LOAD_SUCCESS;
00303 }
00304 
00305 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
00306       .load = load_module,
00307       .unload = unload_module,
00308       .reload = reload,
00309       .load_pri = AST_MODPRI_CHANNEL_DEPEND
00310    );

Generated on Wed Apr 6 11:29:47 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7