config_parser.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 config parsing functions and unit tests
00020  */
00021 
00022 /*** MODULEINFO
00023    <support_level>extended</support_level>
00024  ***/
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 420563 $")
00029 
00030 #include "include/sip.h"
00031 #include "include/config_parser.h"
00032 #include "include/sip_utils.h"
00033 
00034 /*! \brief Parse register=> line in sip.conf
00035  *
00036  * \retval 0 on success
00037  * \retval -1 on failure
00038  */
00039 int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const char *value, int lineno)
00040 {
00041    int portnum = 0;
00042    int domainport = 0;
00043    enum ast_transport transport = AST_TRANSPORT_UDP;
00044    char buf[256] = "";
00045    char *userpart = NULL, *hostpart = NULL;
00046    /* register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] */
00047    AST_DECLARE_APP_ARGS(pre1,
00048       AST_APP_ARG(peer);
00049       AST_APP_ARG(userpart);
00050    );
00051    AST_DECLARE_APP_ARGS(pre2,
00052       AST_APP_ARG(transport);
00053       AST_APP_ARG(blank);
00054       AST_APP_ARG(userpart);
00055    );
00056    AST_DECLARE_APP_ARGS(user1,
00057       AST_APP_ARG(userpart);
00058       AST_APP_ARG(secret);
00059       AST_APP_ARG(authuser);
00060    );
00061    AST_DECLARE_APP_ARGS(user2,
00062       AST_APP_ARG(user);
00063       AST_APP_ARG(domain);
00064    );
00065    AST_DECLARE_APP_ARGS(user3,
00066       AST_APP_ARG(authuser);
00067       AST_APP_ARG(domainport);
00068    );
00069    AST_DECLARE_APP_ARGS(host1,
00070       AST_APP_ARG(hostpart);
00071       AST_APP_ARG(expiry);
00072    );
00073    AST_DECLARE_APP_ARGS(host2,
00074       AST_APP_ARG(hostpart);
00075       AST_APP_ARG(extension);
00076    );
00077    AST_DECLARE_APP_ARGS(host3,
00078       AST_APP_ARG(host);
00079       AST_APP_ARG(port);
00080    );
00081 
00082    if (!value) {
00083       return -1;
00084    }
00085 
00086    if (!reg) {
00087       return -1;
00088    }
00089    ast_copy_string(buf, value, sizeof(buf));
00090 
00091    /*! register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry]
00092     * becomes
00093     *   userpart => [peer?][transport://]user[@domain][:secret[:authuser]]
00094     *   hostpart => host[:port][/extension][~expiry]
00095     */
00096    if ((hostpart = strrchr(buf, '@'))) {
00097       *hostpart++ = '\0';
00098       userpart = buf;
00099    }
00100 
00101    if (ast_strlen_zero(userpart) || ast_strlen_zero(hostpart)) {
00102       ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno);
00103       return -1;
00104    }
00105 
00106    /*!
00107     * pre1.peer => peer
00108     * pre1.userpart => [transport://]user[@domain][:secret[:authuser]]
00109     * hostpart => host[:port][/extension][~expiry]
00110     */
00111    AST_NONSTANDARD_RAW_ARGS(pre1, userpart, '?');
00112    if (ast_strlen_zero(pre1.userpart)) {
00113       pre1.userpart = pre1.peer;
00114       pre1.peer = NULL;
00115    }
00116 
00117    /*!
00118     * pre1.peer => peer
00119     * pre2.transport = transport
00120     * pre2.userpart => user[@domain][:secret[:authuser]]
00121     * hostpart => host[:port][/extension][~expiry]
00122     */
00123    AST_NONSTANDARD_RAW_ARGS(pre2, pre1.userpart, '/');
00124    if (ast_strlen_zero(pre2.userpart)) {
00125       pre2.userpart = pre2.transport;
00126       pre2.transport = NULL;
00127    } else {
00128       pre2.transport[strlen(pre2.transport) - 1] = '\0'; /* Remove trailing : */
00129    }
00130 
00131    if (!ast_strlen_zero(pre2.blank)) {
00132       ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno);
00133       return -1;
00134    }
00135 
00136    /*!
00137     * pre1.peer => peer
00138     * pre2.transport = transport
00139     * user1.userpart => user[@domain]
00140     * user1.secret => secret
00141     * user1.authuser => authuser
00142     * hostpart => host[:port][/extension][~expiry]
00143     */
00144    AST_NONSTANDARD_RAW_ARGS(user1, pre2.userpart, ':');
00145 
00146    /*!
00147     * pre1.peer => peer
00148     * pre2.transport = transport
00149     * user1.userpart => user[@domain]
00150     * user1.secret => secret
00151     * user1.authuser => authuser
00152     * host1.hostpart => host[:port][/extension]
00153     * host1.expiry => [expiry]
00154     */
00155    AST_NONSTANDARD_RAW_ARGS(host1, hostpart, '~');
00156 
00157    /*!
00158     * pre1.peer => peer
00159     * pre2.transport = transport
00160     * user1.userpart => user[@domain]
00161     * user1.secret => secret
00162     * user1.authuser => authuser
00163     * host2.hostpart => host[:port]
00164     * host2.extension => [extension]
00165     * host1.expiry => [expiry]
00166     */
00167    AST_NONSTANDARD_RAW_ARGS(host2, host1.hostpart, '/');
00168 
00169    /*!
00170     * pre1.peer => peer
00171     * pre2.transport = transport
00172     * user1.userpart => user[@domain]
00173     * user1.secret => secret
00174     * user1.authuser => authuser
00175     * host3.host => host
00176     * host3.port => port
00177     * host2.extension => extension
00178     * host1.expiry => expiry
00179     */
00180    AST_NONSTANDARD_RAW_ARGS(host3, host2.hostpart, ':');
00181 
00182    /*!
00183      * pre1.peer => peer
00184      * pre2.transport = transport
00185      * user2.user => user
00186      * user2.domain => domain
00187      * user1.secret => secret
00188      * user1.authuser => authuser
00189      * host3.host => host
00190      * host3.port => port
00191      * host2.extension => extension
00192      * host1.expiry => expiry
00193     */
00194    AST_NONSTANDARD_RAW_ARGS(user2, user1.userpart, '@');
00195 
00196    /*!
00197      * pre1.peer => peer
00198      * pre2.transport = transport
00199      * user2.user => user
00200      * user2.domain => domain
00201      * user1.secret => secret
00202      * user3.authuser => authuser
00203      * user3.domainport => domainport
00204      * host3.host => host
00205      * host3.port => port
00206      * host2.extension => extension
00207      * host1.expiry => expiry
00208     */
00209    AST_NONSTANDARD_RAW_ARGS(user3, user1.authuser, ':');
00210 
00211    /* Reordering needed due to fields being [(:secret[:username])|(:regdomainport:secret:username)]
00212       but parsing being [secret[:username[:regdomainport]]] */
00213    if (user3.argc == 2) {
00214       char *reorder = user3.domainport;
00215       user3.domainport = user1.secret;
00216       user1.secret = user3.authuser;
00217       user3.authuser = reorder;
00218    }
00219 
00220    if (host3.port) {
00221       if (!(portnum = port_str2int(host3.port, 0))) {
00222          ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", host3.port, lineno);
00223       }
00224    }
00225    if (user3.domainport) {
00226       if (!(domainport = port_str2int(user3.domainport, 0))) {
00227          ast_log(LOG_NOTICE, "'%s' is not a valid domain port number on line %d of sip.conf. using default.\n", user3.domainport, lineno);
00228       }
00229    }
00230 
00231    /* set transport type */
00232    if (!pre2.transport) {
00233       transport = AST_TRANSPORT_UDP;
00234    } else if (!strncasecmp(pre2.transport, "tcp", 3)) {
00235       transport = AST_TRANSPORT_TCP;
00236    } else if (!strncasecmp(pre2.transport, "tls", 3)) {
00237       transport = AST_TRANSPORT_TLS;
00238    } else if (!strncasecmp(pre2.transport, "udp", 3)) {
00239       transport = AST_TRANSPORT_UDP;
00240    } else {
00241       transport = AST_TRANSPORT_UDP;
00242       ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", pre2.transport, lineno);
00243    }
00244 
00245    /* if no portnum specified, set default for transport */
00246    if (!portnum) {
00247       if (transport == AST_TRANSPORT_TLS) {
00248          portnum = STANDARD_TLS_PORT;
00249       } else {
00250          portnum = STANDARD_SIP_PORT;
00251       }
00252    }
00253 
00254    /* copy into sip_registry object */
00255    ast_string_field_set(reg, callback, ast_strip_quoted(S_OR(host2.extension, "s"), "\"", "\""));
00256    ast_string_field_set(reg, username, ast_strip_quoted(S_OR(user2.user, ""), "\"", "\""));
00257    ast_string_field_set(reg, hostname, ast_strip_quoted(S_OR(host3.host, ""), "\"", "\""));
00258    ast_string_field_set(reg, authuser, ast_strip_quoted(S_OR(user3.authuser, ""), "\"", "\""));
00259    ast_string_field_set(reg, secret, ast_strip_quoted(S_OR(user1.secret, ""), "\"", "\""));
00260    ast_string_field_set(reg, peername, ast_strip_quoted(S_OR(pre1.peer, ""), "\"", "\""));
00261    ast_string_field_set(reg, regdomain, ast_strip_quoted(S_OR(user2.domain, ""), "\"", "\""));
00262 
00263    reg->transport = transport;
00264    reg->timeout = reg->expire = -1;
00265    reg->portno = portnum;
00266    reg->regdomainport = domainport;
00267    reg->callid_valid = FALSE;
00268    reg->ocseq = INITIAL_CSEQ;
00269    reg->refresh = reg->expiry = reg->configured_expiry = (host1.expiry ? atoi(ast_strip_quoted(host1.expiry, "\"", "\"")) : default_expiry);
00270 
00271    return 0;
00272 }
00273 
00274 AST_TEST_DEFINE(sip_parse_register_line_test)
00275 {
00276    int res = AST_TEST_PASS;
00277    struct sip_registry *reg;
00278    int default_expiry = 120;
00279    const char *reg1 = "name@domain";
00280    const char *reg2 = "name:pass@domain";
00281    const char *reg3 = "name@namedomain:pass:authuser@domain";
00282    const char *reg4 = "name@namedomain:pass:authuser@domain/extension";
00283    const char *reg5 = "tcp://name@namedomain:pass:authuser@domain/extension";
00284    const char *reg6 = "tls://name@namedomain:pass:authuser@domain/extension~111";
00285    const char *reg7 = "peer?tcp://name@namedomain:pass:authuser@domain:1234/extension~111";
00286    const char *reg8 = "peer?name@namedomain:pass:authuser@domain:1234/extension~111";
00287    const char *reg9 = "peer?name:pass:authuser:1234/extension~111";
00288    const char *reg10 = "@domin:1234";
00289    const char *reg12 = "name@namedomain:4321:pass:authuser@domain";
00290    const char *reg13 = "name@namedomain:4321::@domain";
00291 
00292    switch (cmd) {
00293    case TEST_INIT:
00294       info->name = "sip_parse_register_line_test";
00295       info->category = "/channels/chan_sip/";
00296       info->summary = "tests sip register line parsing";
00297       info->description =
00298                      "Tests parsing of various register line configurations. "
00299                      "Verifies output matches expected behavior.";
00300       return AST_TEST_NOT_RUN;
00301    case TEST_EXECUTE:
00302       break;
00303    }
00304 
00305    /* ---Test reg 1, simple config --- */
00306    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00307       goto alloc_fail;
00308    } else if (
00309        sip_parse_register_line(reg, default_expiry, reg1, 1) ||
00310       strcmp(reg->callback, "s")           ||
00311       strcmp(reg->username, "name")       ||
00312       strcmp(reg->regdomain, "")          ||
00313       strcmp(reg->hostname, "domain")     ||
00314       strcmp(reg->authuser, "")           ||
00315       strcmp(reg->secret, "")             ||
00316       strcmp(reg->peername, "")           ||
00317       reg->transport != AST_TRANSPORT_UDP ||
00318       reg->timeout != -1                  ||
00319       reg->expire != -1                   ||
00320       reg->refresh != default_expiry ||
00321       reg->expiry != default_expiry ||
00322       reg->configured_expiry != default_expiry ||
00323       reg->portno != STANDARD_SIP_PORT    ||
00324       (reg->regdomainport)                ||
00325       reg->callid_valid != FALSE          ||
00326       reg->ocseq != INITIAL_CSEQ) {
00327 
00328       ast_test_status_update(test, "Test 1: simple config failed\n");
00329       res = AST_TEST_FAIL;
00330    }
00331    ast_string_field_free_memory(reg);
00332    ast_free(reg);
00333 
00334    /* ---Test reg 2, add secret --- */
00335    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00336       goto alloc_fail;
00337    } else if (
00338        sip_parse_register_line(reg, default_expiry, reg2, 1) ||
00339       strcmp(reg->callback, "s")           ||
00340       strcmp(reg->username, "name")       ||
00341       strcmp(reg->regdomain, "")          ||
00342       strcmp(reg->hostname, "domain")     ||
00343       strcmp(reg->authuser, "")           ||
00344       strcmp(reg->secret, "pass")         ||
00345       strcmp(reg->peername, "")           ||
00346       reg->transport != AST_TRANSPORT_UDP ||
00347       reg->timeout != -1                  ||
00348       reg->expire != -1                   ||
00349       reg->refresh != default_expiry ||
00350       reg->expiry != default_expiry ||
00351       reg->configured_expiry != default_expiry ||
00352       reg->portno != STANDARD_SIP_PORT    ||
00353       (reg->regdomainport)                ||
00354       reg->callid_valid != FALSE          ||
00355       reg->ocseq != INITIAL_CSEQ) {
00356 
00357       ast_test_status_update(test,  "Test 2: add secret failed\n");
00358       res = AST_TEST_FAIL;
00359    }
00360    ast_string_field_free_memory(reg);
00361    ast_free(reg);
00362 
00363    /* ---Test reg 3, add userdomain and authuser --- */
00364    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00365       goto alloc_fail;
00366    } else if (
00367        sip_parse_register_line(reg, default_expiry, reg3, 1) ||
00368       strcmp(reg->callback, "s")           ||
00369       strcmp(reg->username, "name") ||
00370       strcmp(reg->regdomain, "namedomain") ||
00371       strcmp(reg->hostname, "domain")     ||
00372       strcmp(reg->authuser, "authuser")           ||
00373       strcmp(reg->secret, "pass")         ||
00374       strcmp(reg->peername, "")           ||
00375       reg->transport != AST_TRANSPORT_UDP ||
00376       reg->timeout != -1                  ||
00377       reg->expire != -1                   ||
00378       reg->refresh != default_expiry ||
00379       reg->expiry != default_expiry ||
00380       reg->configured_expiry != default_expiry ||
00381       reg->portno != STANDARD_SIP_PORT    ||
00382       (reg->regdomainport)                ||
00383       reg->callid_valid != FALSE          ||
00384       reg->ocseq != INITIAL_CSEQ) {
00385 
00386       ast_test_status_update(test, "Test 3: add userdomain and authuser failed\n");
00387       res = AST_TEST_FAIL;
00388    }
00389    ast_string_field_free_memory(reg);
00390    ast_free(reg);
00391 
00392    /* ---Test reg 4, add callback extension --- */
00393    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00394       goto alloc_fail;
00395    } else if (
00396        sip_parse_register_line(reg, default_expiry, reg4, 1) ||
00397       strcmp(reg->callback, "extension")           ||
00398       strcmp(reg->username, "name") ||
00399       strcmp(reg->regdomain, "namedomain") ||
00400       strcmp(reg->hostname, "domain")     ||
00401       strcmp(reg->authuser, "authuser")           ||
00402       strcmp(reg->secret, "pass")         ||
00403       strcmp(reg->peername, "")           ||
00404       reg->transport != AST_TRANSPORT_UDP ||
00405       reg->timeout != -1                  ||
00406       reg->expire != -1                   ||
00407       reg->refresh != default_expiry ||
00408       reg->expiry != default_expiry ||
00409       reg->configured_expiry != default_expiry ||
00410       reg->portno != STANDARD_SIP_PORT    ||
00411       (reg->regdomainport)                ||
00412       reg->callid_valid != FALSE          ||
00413       reg->ocseq != INITIAL_CSEQ) {
00414 
00415       ast_test_status_update(test, "Test 4: add callback extension failed\n");
00416       res = AST_TEST_FAIL;
00417    }
00418    ast_string_field_free_memory(reg);
00419    ast_free(reg);
00420 
00421    /* ---Test reg 5, add transport --- */
00422    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00423       goto alloc_fail;
00424    } else if (
00425        sip_parse_register_line(reg, default_expiry, reg5, 1) ||
00426       strcmp(reg->callback, "extension")           ||
00427       strcmp(reg->username, "name") ||
00428       strcmp(reg->regdomain, "namedomain") ||
00429       strcmp(reg->hostname, "domain")     ||
00430       strcmp(reg->authuser, "authuser")           ||
00431       strcmp(reg->secret, "pass")         ||
00432       strcmp(reg->peername, "")           ||
00433       reg->transport != AST_TRANSPORT_TCP ||
00434       reg->timeout != -1                  ||
00435       reg->expire != -1                   ||
00436       reg->refresh != default_expiry ||
00437       reg->expiry != default_expiry ||
00438       reg->configured_expiry != default_expiry ||
00439       reg->portno != STANDARD_SIP_PORT    ||
00440       (reg->regdomainport)                ||
00441       reg->callid_valid != FALSE          ||
00442       reg->ocseq != INITIAL_CSEQ) {
00443 
00444       ast_test_status_update(test, "Test 5: add transport failed\n");
00445       res = AST_TEST_FAIL;
00446    }
00447    ast_string_field_free_memory(reg);
00448    ast_free(reg);
00449 
00450    /* ---Test reg 6, change to tls transport, add expiry  --- */
00451    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00452       goto alloc_fail;
00453    } else if (
00454        sip_parse_register_line(reg, default_expiry, reg6, 1) ||
00455       strcmp(reg->callback, "extension")           ||
00456       strcmp(reg->username, "name") ||
00457       strcmp(reg->regdomain, "namedomain") ||
00458       strcmp(reg->hostname, "domain")     ||
00459       strcmp(reg->authuser, "authuser")           ||
00460       strcmp(reg->secret, "pass")         ||
00461       strcmp(reg->peername, "")           ||
00462       reg->transport != AST_TRANSPORT_TLS ||
00463       reg->timeout != -1                  ||
00464       reg->expire != -1                   ||
00465       reg->refresh != 111 ||
00466       reg->expiry != 111 ||
00467       reg->configured_expiry != 111 ||
00468       reg->portno != STANDARD_TLS_PORT    ||
00469       (reg->regdomainport)                ||
00470       reg->callid_valid != FALSE          ||
00471       reg->ocseq != INITIAL_CSEQ) {
00472 
00473       ast_test_status_update(test, "Test 6: change to tls transport and add expiry failed\n");
00474       res = AST_TEST_FAIL;
00475    }
00476    ast_string_field_free_memory(reg);
00477    ast_free(reg);
00478 
00479    /* ---Test reg 7, change transport to tcp, add custom port, and add peer --- */
00480    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00481       goto alloc_fail;
00482    } else if (
00483        sip_parse_register_line(reg, default_expiry, reg7, 1) ||
00484       strcmp(reg->callback, "extension")           ||
00485       strcmp(reg->username, "name") ||
00486       strcmp(reg->regdomain, "namedomain") ||
00487       strcmp(reg->hostname, "domain")     ||
00488       strcmp(reg->authuser, "authuser")           ||
00489       strcmp(reg->secret, "pass")         ||
00490       strcmp(reg->peername, "peer")           ||
00491       reg->transport != AST_TRANSPORT_TCP ||
00492       reg->timeout != -1                  ||
00493       reg->expire != -1                   ||
00494       reg->refresh != 111 ||
00495       reg->expiry != 111 ||
00496       reg->configured_expiry != 111 ||
00497       reg->portno != 1234    ||
00498       (reg->regdomainport)                ||
00499       reg->callid_valid != FALSE          ||
00500       reg->ocseq != INITIAL_CSEQ) {
00501 
00502       ast_test_status_update(test, "Test 7, change transport to tcp, add custom port, and add peer failed.\n");
00503       res = AST_TEST_FAIL;
00504    }
00505    ast_string_field_free_memory(reg);
00506    ast_free(reg);
00507 
00508    /* ---Test reg 8, remove transport --- */
00509    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00510       goto alloc_fail;
00511    } else if (
00512        sip_parse_register_line(reg, default_expiry, reg8, 1) ||
00513       strcmp(reg->callback, "extension")           ||
00514       strcmp(reg->username, "name") ||
00515       strcmp(reg->regdomain, "namedomain") ||
00516       strcmp(reg->hostname, "domain")     ||
00517       strcmp(reg->authuser, "authuser")           ||
00518       strcmp(reg->secret, "pass")         ||
00519       strcmp(reg->peername, "peer")           ||
00520       reg->transport != AST_TRANSPORT_UDP ||
00521       reg->timeout != -1                  ||
00522       reg->expire != -1                   ||
00523       reg->refresh != 111 ||
00524       reg->expiry != 111 ||
00525       reg->configured_expiry != 111 ||
00526       reg->portno != 1234    ||
00527       (reg->regdomainport)                ||
00528       reg->callid_valid != FALSE          ||
00529       reg->ocseq != INITIAL_CSEQ) {
00530 
00531       ast_test_status_update(test, "Test 8, remove transport failed.\n");
00532       res = AST_TEST_FAIL;
00533    }
00534    ast_string_field_free_memory(reg);
00535    ast_free(reg);
00536 
00537    /* ---Test reg 9, missing domain, expected to fail --- */
00538    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00539       goto alloc_fail;
00540    } else if (!sip_parse_register_line(reg, default_expiry, reg9, 1)) {
00541       ast_test_status_update(test,
00542             "Test 9, missing domain, expected to fail but did not.\n");
00543       res = AST_TEST_FAIL;
00544    }
00545    ast_string_field_free_memory(reg);
00546    ast_free(reg);
00547 
00548    /* ---Test reg 10,  missing user, expected to fail --- */
00549    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00550       goto alloc_fail;
00551    } else if (!sip_parse_register_line(reg, default_expiry, reg10, 1)) {
00552       ast_test_status_update(test,
00553             "Test 10, missing user expected to fail but did not\n");
00554       res = AST_TEST_FAIL;
00555    }
00556    ast_string_field_free_memory(reg);
00557    ast_free(reg);
00558 
00559    /* ---Test reg 11, no registry object, expected to fail--- */
00560    if (!sip_parse_register_line(NULL, default_expiry, reg1, 1)) {
00561       ast_test_status_update(test,
00562             "Test 11, no registry object, expected to fail but did not.\n");
00563       res = AST_TEST_FAIL;
00564    }
00565 
00566    /* ---Test reg 12,  no registry line, expected to fail --- */
00567    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00568       goto alloc_fail;
00569    } else if (!sip_parse_register_line(reg, default_expiry, NULL, 1)) {
00570 
00571       ast_test_status_update(test,
00572             "Test 12, NULL register line expected to fail but did not.\n");
00573       res = AST_TEST_FAIL;
00574    }
00575    ast_string_field_free_memory(reg);
00576    ast_free(reg);
00577 
00578    /* ---Test reg13, add domain port --- */
00579    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00580       goto alloc_fail;
00581    } else if (
00582       sip_parse_register_line(reg, default_expiry, reg12, 1) ||
00583       strcmp(reg->callback, "s")           ||
00584       strcmp(reg->username, "name") ||
00585       strcmp(reg->regdomain, "namedomain") ||
00586       strcmp(reg->hostname, "domain")     ||
00587       strcmp(reg->authuser, "authuser")           ||
00588       strcmp(reg->secret, "pass")         ||
00589       strcmp(reg->peername, "")           ||
00590       reg->transport != AST_TRANSPORT_UDP ||
00591       reg->timeout != -1                  ||
00592       reg->expire != -1                   ||
00593       reg->refresh != default_expiry ||
00594       reg->expiry != default_expiry ||
00595       reg->configured_expiry != default_expiry ||
00596       reg->portno != STANDARD_SIP_PORT    ||
00597       reg->regdomainport != 4321          ||
00598       reg->callid_valid != FALSE          ||
00599       reg->ocseq != INITIAL_CSEQ) {
00600 
00601       ast_test_status_update(test, "Test 13, add domain port failed.\n");
00602       res = AST_TEST_FAIL;
00603    }
00604    ast_string_field_free_memory(reg);
00605    ast_free(reg);
00606 
00607    /* ---Test reg14, domain port without secret --- */
00608    if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
00609       goto alloc_fail;
00610    } else if (
00611       sip_parse_register_line(reg, default_expiry, reg13, 1) ||
00612       strcmp(reg->callback, "s")           ||
00613       strcmp(reg->username, "name") ||
00614       strcmp(reg->regdomain, "namedomain") ||
00615       strcmp(reg->hostname, "domain")     ||
00616       strcmp(reg->authuser, "")           ||
00617       strcmp(reg->secret, "")         ||
00618       strcmp(reg->peername, "")           ||
00619       reg->transport != AST_TRANSPORT_UDP ||
00620       reg->timeout != -1                  ||
00621       reg->expire != -1                   ||
00622       reg->refresh != default_expiry ||
00623       reg->expiry != default_expiry ||
00624       reg->configured_expiry != default_expiry ||
00625       reg->portno != STANDARD_SIP_PORT    ||
00626       reg->regdomainport != 4321          ||
00627       reg->callid_valid != FALSE          ||
00628       reg->ocseq != INITIAL_CSEQ) {
00629 
00630       ast_test_status_update(test, "Test 14, domain port without secret failed.\n");
00631       res = AST_TEST_FAIL;
00632    }
00633    ast_string_field_free_memory(reg);
00634    ast_free(reg);
00635 
00636 
00637    return res;
00638 
00639 alloc_fail:
00640    ast_test_status_update(test, "Out of memory. \n");
00641    return res;
00642 }
00643 
00644 int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum ast_transport *transport)
00645 {
00646    char *port;
00647 
00648    if (ast_strlen_zero(line)) {
00649       *hostname = NULL;
00650       return -1;
00651    }
00652    if ((*hostname = strstr(line, "://"))) {
00653       *hostname += 3;
00654 
00655       if (!strncasecmp(line, "tcp", 3)) {
00656          *transport = AST_TRANSPORT_TCP;
00657       } else if (!strncasecmp(line, "tls", 3)) {
00658          *transport = AST_TRANSPORT_TLS;
00659       } else if (!strncasecmp(line, "udp", 3)) {
00660          *transport = AST_TRANSPORT_UDP;
00661       } else if (lineno) {
00662          ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", line, lineno);
00663       } else {
00664          ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type in sip config. defaulting to udp.\n", line);
00665       }
00666    } else {
00667       *hostname = line;
00668       *transport = AST_TRANSPORT_UDP;
00669    }
00670 
00671    if ((line = strrchr(*hostname, '@')))
00672       line++;
00673    else
00674       line = *hostname;
00675 
00676    if (ast_sockaddr_split_hostport(line, hostname, &port, 0) == 0) {
00677       if (lineno) {
00678          ast_log(LOG_WARNING, "Cannot parse host '%s' on line %d of sip.conf.\n",
00679             line, lineno);
00680       } else {
00681          ast_log(LOG_WARNING, "Cannot parse host '%s' in sip config.\n", line);
00682       }
00683       return -1;
00684    }
00685 
00686    if (port) {
00687       if (!sscanf(port, "%5d", portnum)) {
00688          if (lineno) {
00689             ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", port, lineno);
00690          } else {
00691             ast_log(LOG_NOTICE, "'%s' is not a valid port number in sip config. using default.\n", port);
00692          }
00693          port = NULL;
00694       }
00695    }
00696 
00697    if (!port) {
00698       if (*transport & AST_TRANSPORT_TLS) {
00699          *portnum = STANDARD_TLS_PORT;
00700       } else {
00701          *portnum = STANDARD_SIP_PORT;
00702       }
00703    }
00704 
00705    return 0;
00706 }
00707 
00708 AST_TEST_DEFINE(sip_parse_host_line_test)
00709 {
00710    int res = AST_TEST_PASS;
00711    char *host;
00712    int port;
00713    enum ast_transport transport;
00714    char host1[] = "www.blah.com";
00715    char host2[] = "tcp://www.blah.com";
00716    char host3[] = "tls://10.10.10.10";
00717    char host4[] = "tls://10.10.10.10:1234";
00718    char host5[] = "10.10.10.10:1234";
00719 
00720    switch (cmd) {
00721    case TEST_INIT:
00722       info->name = "sip_parse_host_line_test";
00723       info->category = "/channels/chan_sip/";
00724       info->summary = "tests sip.conf host line parsing";
00725       info->description =
00726                      "Tests parsing of various host line configurations. "
00727                      "Verifies output matches expected behavior.";
00728       return AST_TEST_NOT_RUN;
00729    case TEST_EXECUTE:
00730       break;
00731    }
00732 
00733    /* test 1, simple host */
00734    sip_parse_host(host1, 1, &host, &port, &transport);
00735    if (port != STANDARD_SIP_PORT ||
00736          ast_strlen_zero(host) || strcmp(host, "www.blah.com") ||
00737          transport != AST_TRANSPORT_UDP) {
00738       ast_test_status_update(test, "Test 1: simple host failed.\n");
00739       res = AST_TEST_FAIL;
00740    }
00741 
00742    /* test 2, add tcp transport */
00743    sip_parse_host(host2, 1, &host, &port, &transport);
00744    if (port != STANDARD_SIP_PORT ||
00745          ast_strlen_zero(host) || strcmp(host, "www.blah.com") ||
00746          transport != AST_TRANSPORT_TCP) {
00747       ast_test_status_update(test, "Test 2: tcp host failed.\n");
00748       res = AST_TEST_FAIL;
00749    }
00750 
00751    /* test 3, add tls transport */
00752    sip_parse_host(host3, 1, &host, &port, &transport);
00753    if (port != STANDARD_TLS_PORT ||
00754          ast_strlen_zero(host) || strcmp(host, "10.10.10.10") ||
00755          transport != AST_TRANSPORT_TLS) {
00756       ast_test_status_update(test, "Test 3: tls host failed. \n");
00757       res = AST_TEST_FAIL;
00758    }
00759 
00760    /* test 4, add custom port with tls */
00761    sip_parse_host(host4, 1, &host, &port, &transport);
00762    if (port != 1234 || ast_strlen_zero(host) ||
00763          strcmp(host, "10.10.10.10") ||
00764          transport != AST_TRANSPORT_TLS) {
00765       ast_test_status_update(test, "Test 4: tls host with custom port failed.\n");
00766       res = AST_TEST_FAIL;
00767    }
00768 
00769    /* test 5, simple host with custom port */
00770    sip_parse_host(host5, 1, &host, &port, &transport);
00771    if (port != 1234 || ast_strlen_zero(host) ||
00772          strcmp(host, "10.10.10.10") ||
00773          transport != AST_TRANSPORT_UDP) {
00774       ast_test_status_update(test, "Test 5: simple host with custom port failed.\n");
00775       res = AST_TEST_FAIL;
00776    }
00777 
00778    /* test 6, expected failure with NULL input */
00779    if (!sip_parse_host(NULL, 1, &host, &port, &transport)) {
00780       ast_test_status_update(test, "Test 6: expected error on NULL input did not occur.\n");
00781       res = AST_TEST_FAIL;
00782    }
00783 
00784    return res;
00785 
00786 }
00787 
00788 /*! \brief Parse the comma-separated nat= option values */
00789 void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags)
00790 {
00791    char *parse, *this;
00792 
00793    if (!(parse = ast_strdupa(value))) {
00794       return;
00795    }
00796 
00797    /* Since we need to completely override the general settings if we are being called
00798     * later for a peer, always set the flags for all options on the mask */
00799    ast_set_flag(&mask[0], SIP_NAT_FORCE_RPORT);
00800    ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
00801    ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_RPORT);
00802    ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
00803 
00804    while ((this = strsep(&parse, ","))) {
00805       if (ast_false(this)) {
00806          ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
00807          ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
00808          ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
00809          ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
00810          break; /* It doesn't make sense to have no + something else */
00811       } else if (!strcasecmp(this, "yes")) {
00812          ast_log(LOG_WARNING, "nat=yes is deprecated, use nat=force_rport,comedia instead\n");
00813          ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
00814          ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
00815          ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
00816          ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
00817          break; /* It doesn't make sense to have yes + something else */
00818       } else if (!strcasecmp(this, "force_rport") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
00819          ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
00820       } else if (!strcasecmp(this, "comedia") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
00821          ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
00822       } else if (!strcasecmp(this, "auto_force_rport")) {
00823          ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
00824          /* In case someone did something dumb like nat=force_rport,auto_force_rport */
00825          ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
00826       } else if (!strcasecmp(this, "auto_comedia")) {
00827          ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
00828          /* In case someone did something dumb like nat=comedia,auto_comedia*/
00829          ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
00830       }
00831    }
00832 }
00833 
00834 #define TEST_FORCE_RPORT      1 << 0
00835 #define TEST_COMEDIA          1 << 1
00836 #define TEST_AUTO_FORCE_RPORT 1 << 2
00837 #define TEST_AUTO_COMEDIA     1 << 3
00838 static int match_nat_options(int val, struct ast_flags *flags)
00839 {
00840    if ((!ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT)) != !(val & TEST_FORCE_RPORT)) {
00841       return 0;
00842    }
00843    if (!ast_test_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP) != !(val & TEST_COMEDIA)) {
00844       return 0;
00845    }
00846    if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT) != !(val & TEST_AUTO_FORCE_RPORT)) {
00847       return 0;
00848    }
00849    if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) != !(val & TEST_AUTO_COMEDIA)) {
00850       return 0;
00851    }
00852    return 1;
00853 }
00854 
00855 AST_TEST_DEFINE(sip_parse_nat_test)
00856 {
00857    int i, res = AST_TEST_PASS;
00858    struct ast_flags mask[3] = {{0}}, flags[3] = {{0}};
00859    struct {
00860       const char *str;
00861       int i;
00862    } options[] = {
00863       { "yes", TEST_FORCE_RPORT | TEST_COMEDIA },
00864       { "no", 0 },
00865       { "force_rport", TEST_FORCE_RPORT },
00866       { "comedia", TEST_COMEDIA },
00867       { "auto_force_rport", TEST_AUTO_FORCE_RPORT },
00868       { "auto_comedia", TEST_AUTO_COMEDIA },
00869       { "force_rport,auto_force_rport", TEST_AUTO_FORCE_RPORT },
00870       { "auto_force_rport,force_rport", TEST_AUTO_FORCE_RPORT },
00871       { "comedia,auto_comedia", TEST_AUTO_COMEDIA },
00872       { "auto_comedia,comedia", TEST_AUTO_COMEDIA },
00873       { "force_rport,comedia", TEST_FORCE_RPORT | TEST_COMEDIA },
00874       { "force_rport,auto_comedia", TEST_FORCE_RPORT | TEST_AUTO_COMEDIA },
00875       { "force_rport,yes,no", TEST_FORCE_RPORT | TEST_COMEDIA },
00876       { "auto_comedia,no,yes", 0 },
00877    };
00878 
00879    switch (cmd) {
00880    case TEST_INIT:
00881       info->name = "sip_parse_nat_test";
00882       info->category = "/channels/chan_sip/";
00883       info->summary = "tests sip.conf nat line parsing";
00884       info->description =
00885                      "Tests parsing of various nat line configurations. "
00886                      "Verifies output matches expected behavior.";
00887       return AST_TEST_NOT_RUN;
00888    case TEST_EXECUTE:
00889       break;
00890    }
00891 
00892    for (i = 0; i < ARRAY_LEN(options); i++) {
00893       sip_parse_nat_option(options[i].str, mask, flags);
00894       if (!match_nat_options(options[i].i, flags)) {
00895          ast_test_status_update(test, "Failed nat=%s\n", options[i].str);
00896          res = AST_TEST_FAIL;
00897       }
00898       memset(flags, 0, sizeof(flags));
00899       memset(mask, 0, sizeof(mask));
00900    }
00901 
00902    return res;
00903 }
00904 /*! \brief SIP test registration */
00905 void sip_config_parser_register_tests(void)
00906 {
00907    AST_TEST_REGISTER(sip_parse_register_line_test);
00908    AST_TEST_REGISTER(sip_parse_host_line_test);
00909    AST_TEST_REGISTER(sip_parse_nat_test);
00910 }
00911 
00912 /*! \brief SIP test registration */
00913 void sip_config_parser_unregister_tests(void)
00914 {
00915    AST_TEST_UNREGISTER(sip_parse_register_line_test);
00916    AST_TEST_UNREGISTER(sip_parse_host_line_test);
00917    AST_TEST_UNREGISTER(sip_parse_nat_test);
00918 }
00919 

Generated on Thu Apr 16 06:27:31 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6