Sat Mar 10 01:54:16 2012

Asterisk developer's documentation


dialplan_functions.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  * See http://www.asterisk.org for more information about
00007  * the Asterisk project. Please do not directly contact
00008  * any of the maintainers of this project for assistance;
00009  * the project provides a web site, mailing lists and IRC
00010  * channels for your use.
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License Version 2. See the LICENSE file
00014  * at the top of the source tree.
00015  */
00016 
00017 /*!
00018  * \file
00019  * \brief sip channel dialplan functions and unit tests
00020  */
00021 
00022 #include "asterisk.h"
00023 
00024 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 310088 $")
00025 
00026 #include <math.h>
00027 
00028 #include "asterisk/channel.h"
00029 #include "asterisk/rtp_engine.h"
00030 #include "asterisk/pbx.h"
00031 #include "asterisk/acl.h"
00032 
00033 #include "include/sip.h"
00034 #include "include/globals.h"
00035 #include "include/dialog.h"
00036 #include "include/dialplan_functions.h"
00037 #include "include/sip_utils.h"
00038 
00039 
00040 int sip_acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen)
00041 {
00042    struct sip_pvt *p = chan->tech_pvt;
00043    char *parse = ast_strdupa(preparse);
00044    int res = 0;
00045    AST_DECLARE_APP_ARGS(args,
00046       AST_APP_ARG(param);
00047       AST_APP_ARG(type);
00048       AST_APP_ARG(field);
00049    );
00050       
00051    /* Check for zero arguments */
00052    if (ast_strlen_zero(parse)) {
00053       ast_log(LOG_ERROR, "Cannot call %s without arguments\n", funcname);
00054       return -1;
00055    }
00056 
00057    AST_STANDARD_APP_ARGS(args, parse);
00058 
00059    /* Sanity check */
00060    if (!IS_SIP_TECH(chan->tech)) {
00061       ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname);
00062       return 0;
00063    }
00064 
00065    memset(buf, 0, buflen);
00066 
00067    if (p == NULL) {
00068       return -1;
00069    }
00070 
00071    if (!strcasecmp(args.param, "peerip")) {
00072       ast_copy_string(buf, ast_sockaddr_isnull(&p->sa) ? "" : ast_sockaddr_stringify_addr(&p->sa), buflen);
00073    } else if (!strcasecmp(args.param, "recvip")) {
00074       ast_copy_string(buf, ast_sockaddr_isnull(&p->recv) ? "" : ast_sockaddr_stringify_addr(&p->recv), buflen);
00075    } else if (!strcasecmp(args.param, "from")) {
00076       ast_copy_string(buf, p->from, buflen);
00077    } else if (!strcasecmp(args.param, "uri")) {
00078       ast_copy_string(buf, p->uri, buflen);
00079    } else if (!strcasecmp(args.param, "useragent")) {
00080       ast_copy_string(buf, p->useragent, buflen);
00081    } else if (!strcasecmp(args.param, "peername")) {
00082       ast_copy_string(buf, p->peername, buflen);
00083    } else if (!strcasecmp(args.param, "t38passthrough")) {
00084       ast_copy_string(buf, (p->t38.state == T38_DISABLED) ? "0" : "1", buflen);
00085    } else if (!strcasecmp(args.param, "rtpdest")) {
00086       struct ast_sockaddr addr;
00087       struct ast_rtp_instance *stream;
00088 
00089       if (ast_strlen_zero(args.type))
00090          args.type = "audio";
00091 
00092       if (!strcasecmp(args.type, "audio"))
00093          stream = p->rtp;
00094       else if (!strcasecmp(args.type, "video"))
00095          stream = p->vrtp;
00096       else if (!strcasecmp(args.type, "text"))
00097          stream = p->trtp;
00098       else
00099          return -1;
00100 
00101       /* Return 0 to suppress a console warning message */
00102       if (!stream) {
00103          return 0;
00104       }
00105 
00106       ast_rtp_instance_get_remote_address(stream, &addr);
00107       snprintf(buf, buflen, "%s", ast_sockaddr_stringify(&addr));
00108    } else if (!strcasecmp(args.param, "rtpsource")) {
00109       struct ast_sockaddr sa;
00110       struct ast_rtp_instance *stream;
00111 
00112       if (ast_strlen_zero(args.type))
00113          args.type = "audio";
00114 
00115       if (!strcasecmp(args.type, "audio"))
00116          stream = p->rtp;
00117       else if (!strcasecmp(args.type, "video"))
00118          stream = p->vrtp;
00119       else if (!strcasecmp(args.type, "text"))
00120          stream = p->trtp;
00121       else
00122          return -1;
00123 
00124       /* Return 0 to suppress a console warning message */
00125       if (!stream) {
00126          return 0;
00127       }
00128 
00129       ast_rtp_instance_get_local_address(stream, &sa);
00130 
00131       if (ast_sockaddr_isnull(&sa)) {
00132          struct ast_sockaddr dest_sa;
00133          ast_rtp_instance_get_remote_address(stream, &dest_sa);
00134          ast_ouraddrfor(&dest_sa, &sa);
00135       }
00136 
00137       snprintf(buf, buflen, "%s", ast_sockaddr_stringify(&sa));
00138    } else if (!strcasecmp(args.param, "rtpqos")) {
00139       struct ast_rtp_instance *rtp = NULL;
00140 
00141       if (ast_strlen_zero(args.type)) {
00142          args.type = "audio";
00143       }
00144 
00145       if (!strcasecmp(args.type, "audio")) {
00146          rtp = p->rtp;
00147       } else if (!strcasecmp(args.type, "video")) {
00148          rtp = p->vrtp;
00149       } else if (!strcasecmp(args.type, "text")) {
00150          rtp = p->trtp;
00151       } else {
00152          return -1;
00153       }
00154 
00155       if (ast_strlen_zero(args.field) || !strcasecmp(args.field, "all")) {
00156          char quality_buf[AST_MAX_USER_FIELD], *quality;
00157 
00158          if (!(quality = ast_rtp_instance_get_quality(rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
00159             return -1;
00160          }
00161 
00162          ast_copy_string(buf, quality_buf, buflen);
00163          return res;
00164       } else {
00165          struct ast_rtp_instance_stats stats;
00166          int i;
00167          struct {
00168             const char *name;
00169             enum { INT, DBL } type;
00170             union {
00171                unsigned int *i4;
00172                double *d8;
00173             };
00174          } lookup[] = {
00175             { "txcount",               INT, { .i4 = &stats.txcount, }, },
00176             { "rxcount",               INT, { .i4 = &stats.rxcount, }, },
00177             { "txjitter",              DBL, { .d8 = &stats.txjitter, }, },
00178             { "rxjitter",              DBL, { .d8 = &stats.rxjitter, }, },
00179             { "remote_maxjitter",      DBL, { .d8 = &stats.remote_maxjitter, }, },
00180             { "remote_minjitter",      DBL, { .d8 = &stats.remote_minjitter, }, },
00181             { "remote_normdevjitter",  DBL, { .d8 = &stats.remote_normdevjitter, }, },
00182             { "remote_stdevjitter",    DBL, { .d8 = &stats.remote_stdevjitter, }, },
00183             { "local_maxjitter",       DBL, { .d8 = &stats.local_maxjitter, }, },
00184             { "local_minjitter",       DBL, { .d8 = &stats.local_minjitter, }, },
00185             { "local_normdevjitter",   DBL, { .d8 = &stats.local_normdevjitter, }, },
00186             { "local_stdevjitter",     DBL, { .d8 = &stats.local_stdevjitter, }, },
00187             { "txploss",               INT, { .i4 = &stats.txploss, }, },
00188             { "rxploss",               INT, { .i4 = &stats.rxploss, }, },
00189             { "remote_maxrxploss",     DBL, { .d8 = &stats.remote_maxrxploss, }, },
00190             { "remote_minrxploss",     DBL, { .d8 = &stats.remote_minrxploss, }, },
00191             { "remote_normdevrxploss", DBL, { .d8 = &stats.remote_normdevrxploss, }, },
00192             { "remote_stdevrxploss",   DBL, { .d8 = &stats.remote_stdevrxploss, }, },
00193             { "local_maxrxploss",      DBL, { .d8 = &stats.local_maxrxploss, }, },
00194             { "local_minrxploss",      DBL, { .d8 = &stats.local_minrxploss, }, },
00195             { "local_normdevrxploss",  DBL, { .d8 = &stats.local_normdevrxploss, }, },
00196             { "local_stdevrxploss",    DBL, { .d8 = &stats.local_stdevrxploss, }, },
00197             { "rtt",                   DBL, { .d8 = &stats.rtt, }, },
00198             { "maxrtt",                DBL, { .d8 = &stats.maxrtt, }, },
00199             { "minrtt",                DBL, { .d8 = &stats.minrtt, }, },
00200             { "normdevrtt",            DBL, { .d8 = &stats.normdevrtt, }, },
00201             { "stdevrtt",              DBL, { .d8 = &stats.stdevrtt, }, },
00202             { "local_ssrc",            INT, { .i4 = &stats.local_ssrc, }, },
00203             { "remote_ssrc",           INT, { .i4 = &stats.remote_ssrc, }, },
00204             { NULL, },
00205          };
00206 
00207          if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) {
00208             return -1;
00209          }
00210 
00211          for (i = 0; !ast_strlen_zero(lookup[i].name); i++) {
00212             if (!strcasecmp(args.field, lookup[i].name)) {
00213                if (lookup[i].type == INT) {
00214                   snprintf(buf, buflen, "%u", *lookup[i].i4);
00215                } else {
00216                   snprintf(buf, buflen, "%f", *lookup[i].d8);
00217                }
00218                return 0;
00219             }
00220          }
00221          ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
00222          return -1;
00223       }
00224    } else if (!strcasecmp(args.param, "secure_signaling")) {
00225       snprintf(buf, buflen, "%s", p->socket.type == SIP_TRANSPORT_TLS ? "1" : "");
00226    } else if (!strcasecmp(args.param, "secure_media")) {
00227       snprintf(buf, buflen, "%s", p->srtp ? "1" : "");
00228    } else {
00229       res = -1;
00230    }
00231    return res;
00232 }
00233 
00234 #ifdef TEST_FRAMEWORK
00235 static int test_sip_rtpqos_1_new(struct ast_rtp_instance *instance, struct sched_context *sched, struct ast_sockaddr *addr, void *data)
00236 {
00237    /* Needed to pass sanity checks */
00238    ast_rtp_instance_set_data(instance, data);
00239    return 0;
00240 }
00241 
00242 static int test_sip_rtpqos_1_destroy(struct ast_rtp_instance *instance)
00243 {
00244    /* Needed to pass sanity checks */
00245    return 0;
00246 }
00247 
00248 static struct ast_frame *test_sip_rtpqos_1_read(struct ast_rtp_instance *instance, int rtcp)
00249 {
00250    /* Needed to pass sanity checks */
00251    return &ast_null_frame;
00252 }
00253 
00254 static int test_sip_rtpqos_1_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
00255 {
00256    /* Needed to pass sanity checks */
00257    return 0;
00258 }
00259 
00260 static int test_sip_rtpqos_1_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat)
00261 {
00262    struct ast_rtp_instance_stats *s = ast_rtp_instance_get_data(instance);
00263    memcpy(stats, s, sizeof(*stats));
00264    return 0;
00265 }
00266 
00267 AST_TEST_DEFINE(test_sip_rtpqos_1)
00268 {
00269    int i, res = AST_TEST_PASS;
00270    struct ast_rtp_engine test_engine = {
00271       .name = "test",
00272       .new = test_sip_rtpqos_1_new,
00273       .destroy = test_sip_rtpqos_1_destroy,
00274       .read = test_sip_rtpqos_1_read,
00275       .write = test_sip_rtpqos_1_write,
00276       .get_stat = test_sip_rtpqos_1_get_stat,
00277    };
00278    struct ast_sockaddr sa = { {0, } };
00279    struct ast_rtp_instance_stats mine = { 0, };
00280    struct sip_pvt *p = NULL;
00281    struct ast_channel *chan = NULL;
00282    struct ast_str *varstr = NULL, *buffer = NULL;
00283    struct {
00284       const char *name;
00285       enum { INT, DBL } type;
00286       union {
00287          unsigned int *i4;
00288          double *d8;
00289       };
00290    } lookup[] = {
00291       { "txcount",               INT, { .i4 = &mine.txcount, }, },
00292       { "rxcount",               INT, { .i4 = &mine.rxcount, }, },
00293       { "txjitter",              DBL, { .d8 = &mine.txjitter, }, },
00294       { "rxjitter",              DBL, { .d8 = &mine.rxjitter, }, },
00295       { "remote_maxjitter",      DBL, { .d8 = &mine.remote_maxjitter, }, },
00296       { "remote_minjitter",      DBL, { .d8 = &mine.remote_minjitter, }, },
00297       { "remote_normdevjitter",  DBL, { .d8 = &mine.remote_normdevjitter, }, },
00298       { "remote_stdevjitter",    DBL, { .d8 = &mine.remote_stdevjitter, }, },
00299       { "local_maxjitter",       DBL, { .d8 = &mine.local_maxjitter, }, },
00300       { "local_minjitter",       DBL, { .d8 = &mine.local_minjitter, }, },
00301       { "local_normdevjitter",   DBL, { .d8 = &mine.local_normdevjitter, }, },
00302       { "local_stdevjitter",     DBL, { .d8 = &mine.local_stdevjitter, }, },
00303       { "txploss",               INT, { .i4 = &mine.txploss, }, },
00304       { "rxploss",               INT, { .i4 = &mine.rxploss, }, },
00305       { "remote_maxrxploss",     DBL, { .d8 = &mine.remote_maxrxploss, }, },
00306       { "remote_minrxploss",     DBL, { .d8 = &mine.remote_minrxploss, }, },
00307       { "remote_normdevrxploss", DBL, { .d8 = &mine.remote_normdevrxploss, }, },
00308       { "remote_stdevrxploss",   DBL, { .d8 = &mine.remote_stdevrxploss, }, },
00309       { "local_maxrxploss",      DBL, { .d8 = &mine.local_maxrxploss, }, },
00310       { "local_minrxploss",      DBL, { .d8 = &mine.local_minrxploss, }, },
00311       { "local_normdevrxploss",  DBL, { .d8 = &mine.local_normdevrxploss, }, },
00312       { "local_stdevrxploss",    DBL, { .d8 = &mine.local_stdevrxploss, }, },
00313       { "rtt",                   DBL, { .d8 = &mine.rtt, }, },
00314       { "maxrtt",                DBL, { .d8 = &mine.maxrtt, }, },
00315       { "minrtt",                DBL, { .d8 = &mine.minrtt, }, },
00316       { "normdevrtt",            DBL, { .d8 = &mine.normdevrtt, }, },
00317       { "stdevrtt",              DBL, { .d8 = &mine.stdevrtt, }, },
00318       { "local_ssrc",            INT, { .i4 = &mine.local_ssrc, }, },
00319       { "remote_ssrc",           INT, { .i4 = &mine.remote_ssrc, }, },
00320       { NULL, },
00321    };
00322 
00323    switch (cmd) {
00324    case TEST_INIT:
00325       info->name = "test_sip_rtpqos";
00326       info->category = "/channels/chan_sip/";
00327       info->summary = "Test retrieval of SIP RTP QOS stats";
00328       info->description =
00329          "Verify values in the RTP instance structure can be accessed through the dialplan.";
00330       return AST_TEST_NOT_RUN;
00331    case TEST_EXECUTE:
00332       break;
00333    }
00334 
00335    ast_rtp_engine_register2(&test_engine, NULL);
00336    /* Have to associate this with a SIP pvt and an ast_channel */
00337    if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY, NULL))) {
00338       res = AST_TEST_NOT_RUN;
00339       goto done;
00340    }
00341 
00342    if (!(p->rtp = ast_rtp_instance_new("test", sched, &bindaddr, &mine))) {
00343       res = AST_TEST_NOT_RUN;
00344       goto done;
00345    }
00346    ast_rtp_instance_set_remote_address(p->rtp, &sa);
00347    if (!(chan = ast_dummy_channel_alloc())) {
00348       res = AST_TEST_NOT_RUN;
00349       goto done;
00350    }
00351    chan->tech = &sip_tech;
00352    chan->tech_pvt = p;
00353    p->owner = chan;
00354 
00355    varstr = ast_str_create(16);
00356    buffer = ast_str_create(16);
00357    if (!varstr || !buffer) {
00358       res = AST_TEST_NOT_RUN;
00359       goto done;
00360    }
00361 
00362    /* Populate "mine" with values, then retrieve them with the CHANNEL dialplan function */
00363    for (i = 0; !ast_strlen_zero(lookup[i].name); i++) {
00364       ast_str_set(&varstr, 0, "${CHANNEL(rtpqos,audio,%s)}", lookup[i].name);
00365       if (lookup[i].type == INT) {
00366          int j;
00367          char cmpstr[256];
00368          for (j = 1; j < 25; j++) {
00369             *lookup[i].i4 = j;
00370             ast_str_substitute_variables(&buffer, 0, chan, ast_str_buffer(varstr));
00371             snprintf(cmpstr, sizeof(cmpstr), "%d", j);
00372             if (strcmp(cmpstr, ast_str_buffer(buffer))) {
00373                res = AST_TEST_FAIL;
00374                ast_test_status_update(test, "%s != %s != %s\n", ast_str_buffer(varstr), cmpstr, ast_str_buffer(buffer));
00375                break;
00376             }
00377          }
00378       } else {
00379          double j, cmpdbl = 0.0;
00380          for (j = 1.0; j < 10.0; j += 0.3) {
00381             *lookup[i].d8 = j;
00382             ast_str_substitute_variables(&buffer, 0, chan, ast_str_buffer(varstr));
00383             if (sscanf(ast_str_buffer(buffer), "%lf", &cmpdbl) != 1 || fabs(j - cmpdbl > .05)) {
00384                res = AST_TEST_FAIL;
00385                ast_test_status_update(test, "%s != %f != %s\n", ast_str_buffer(varstr), j, ast_str_buffer(buffer));
00386                break;
00387             }
00388          }
00389       }
00390    }
00391 
00392 done:
00393    ast_free(varstr);
00394    ast_free(buffer);
00395 
00396    /* This unref will take care of destroying the channel, RTP instance, and SIP pvt */
00397    if (p) {
00398       dialog_unref(p, "Destroy test object");
00399    }
00400    ast_rtp_engine_unregister(&test_engine);
00401    return res;
00402 }
00403 #endif
00404 
00405 /*! \brief SIP test registration */
00406 void sip_dialplan_function_register_tests(void)
00407 {
00408    AST_TEST_REGISTER(test_sip_rtpqos_1);
00409 }
00410 
00411 /*! \brief SIP test registration */
00412 void sip_dialplan_function_unregister_tests(void)
00413 {
00414    AST_TEST_UNREGISTER(test_sip_rtpqos_1);
00415 }
00416 

Generated on Sat Mar 10 01:54:16 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7