Sun Oct 16 2011 08:41:46

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