diff options
Diffstat (limited to 'pjsip/src/test/regc_test.c')
-rw-r--r-- | pjsip/src/test/regc_test.c | 1162 |
1 files changed, 1162 insertions, 0 deletions
diff --git a/pjsip/src/test/regc_test.c b/pjsip/src/test/regc_test.c new file mode 100644 index 00000000..0d668782 --- /dev/null +++ b/pjsip/src/test/regc_test.c @@ -0,0 +1,1162 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "test.h" +#include <pjsip_ua.h> +#include <pjsip.h> +#include <pjlib.h> + +#define THIS_FILE "regc_test.c" + + +/************************************************************************/ +/* A module to inject error into outgoing sending operation */ +static pj_status_t mod_send_on_tx_request(pjsip_tx_data *tdata); + +static struct +{ + pjsip_module mod; + unsigned count; + unsigned count_before_reject; +} send_mod = +{ + { + NULL, NULL, /* prev, next. */ + { "mod-send", 8 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + NULL, /* on_rx_request() */ + NULL, /* on_rx_response() */ + &mod_send_on_tx_request, /* on_tx_request. */ + NULL, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + }, + 0, + 0xFFFF +}; + + +static pj_status_t mod_send_on_tx_request(pjsip_tx_data *tdata) +{ + PJ_UNUSED_ARG(tdata); + + if (++send_mod.count > send_mod.count_before_reject) + return PJ_ECANCELLED; + else + return PJ_SUCCESS; +}; + + +/************************************************************************/ +/* Registrar for testing */ +static pj_bool_t regs_rx_request(pjsip_rx_data *rdata); + +enum contact_op +{ + NONE, /* don't put Contact header */ + EXACT, /* return exact contact */ + MODIFIED, /* return modified Contact header */ +}; + +struct registrar_cfg +{ + pj_bool_t respond; /* should it respond at all */ + unsigned status_code; /* final response status code */ + pj_bool_t authenticate; /* should we authenticate? */ + enum contact_op contact_op; /* What should we do with Contact */ + unsigned expires_param; /* non-zero to put in expires param */ + unsigned expires; /* non-zero to put in Expires header*/ + + pj_str_t more_contacts; /* Additional Contact headers to put*/ +}; + +static struct registrar +{ + pjsip_module mod; + struct registrar_cfg cfg; + unsigned response_cnt; +} registrar = +{ + { + NULL, NULL, /* prev, next. */ + { "registrar", 9 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + ®s_rx_request, /* on_rx_request() */ + NULL, /* on_rx_response() */ + NULL, /* on_tx_request. */ + NULL, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + } +}; + +static pj_bool_t regs_rx_request(pjsip_rx_data *rdata) +{ + pjsip_msg *msg = rdata->msg_info.msg; + pjsip_hdr hdr_list; + int code; + pj_status_t status; + + if (msg->line.req.method.id != PJSIP_REGISTER_METHOD) + return PJ_FALSE; + + if (!registrar.cfg.respond) + return PJ_TRUE; + + pj_list_init(&hdr_list); + + if (registrar.cfg.authenticate && + pjsip_msg_find_hdr(msg, PJSIP_H_AUTHORIZATION, NULL)==NULL) + { + pjsip_generic_string_hdr *hwww; + const pj_str_t hname = pj_str("WWW-Authenticate"); + const pj_str_t hvalue = pj_str("Digest realm=\"test\""); + + hwww = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &hname, + &hvalue); + pj_list_push_back(&hdr_list, hwww); + + code = 401; + + } else { + if (registrar.cfg.contact_op == EXACT || + registrar.cfg.contact_op == MODIFIED) + { + pjsip_hdr *hsrc; + + for (hsrc=msg->hdr.next; hsrc!=&msg->hdr; hsrc=hsrc->next) { + pjsip_contact_hdr *hdst; + + if (hsrc->type != PJSIP_H_CONTACT) + continue; + + hdst = (pjsip_contact_hdr*) + pjsip_hdr_clone(rdata->tp_info.pool, hsrc); + + if (hdst->expires==0) + continue; + + if (registrar.cfg.contact_op == MODIFIED) { + if (PJSIP_URI_SCHEME_IS_SIP(hdst->uri) || + PJSIP_URI_SCHEME_IS_SIPS(hdst->uri)) + { + pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) + pjsip_uri_get_uri(hdst->uri); + sip_uri->host = pj_str("x-modified-host"); + sip_uri->port = 1; + } + } + + if (registrar.cfg.expires_param) + hdst->expires = registrar.cfg.expires_param; + + pj_list_push_back(&hdr_list, hdst); + } + } + + if (registrar.cfg.more_contacts.slen) { + pjsip_generic_string_hdr *hcontact; + const pj_str_t hname = pj_str("Contact"); + + hcontact = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &hname, + ®istrar.cfg.more_contacts); + pj_list_push_back(&hdr_list, hcontact); + } + + if (registrar.cfg.expires) { + pjsip_expires_hdr *hexp; + + hexp = pjsip_expires_hdr_create(rdata->tp_info.pool, + registrar.cfg.expires); + pj_list_push_back(&hdr_list, hexp); + } + + registrar.response_cnt++; + + code = registrar.cfg.status_code; + } + + status = pjsip_endpt_respond(endpt, NULL, rdata, code, NULL, + &hdr_list, NULL, NULL); + pj_assert(status == PJ_SUCCESS); + + return PJ_TRUE; +} + + +/************************************************************************/ +/* Client registration test session */ +struct client +{ + /* Result/expected result */ + int error; + int code; + pj_bool_t have_reg; + int expiration; + unsigned contact_cnt; + pj_bool_t auth; + + /* Commands */ + pj_bool_t destroy_on_cb; + + /* Status */ + pj_bool_t done; + + /* Additional results */ + int interval; + int next_reg; +}; + +/* regc callback */ +static void client_cb(struct pjsip_regc_cbparam *param) +{ + struct client *client = (struct client*) param->token; + pjsip_regc_info info; + pj_status_t status; + + client->done = PJ_TRUE; + + status = pjsip_regc_get_info(param->regc, &info); + pj_assert(status == PJ_SUCCESS); + + client->error = (param->status != PJ_SUCCESS); + client->code = param->code; + + if (client->error) + return; + + client->have_reg = info.auto_reg && info.interval>0 && + param->expiration>0; + client->expiration = param->expiration; + client->contact_cnt = param->contact_cnt; + client->interval = info.interval; + client->next_reg = info.next_reg; + + if (client->destroy_on_cb) + pjsip_regc_destroy(param->regc); +} + + +/* Generic client test session */ +static struct client client_result; +static int do_test(const char *title, + const struct registrar_cfg *srv_cfg, + const struct client *client_cfg, + const pj_str_t *registrar_uri, + unsigned contact_cnt, + const pj_str_t contacts[], + unsigned expires, + pj_bool_t leave_session, + pjsip_regc **p_regc) +{ + pjsip_regc *regc; + unsigned i; + const pj_str_t aor = pj_str("<sip:regc-test@pjsip.org>"); + pjsip_tx_data *tdata; + pj_status_t status; + + PJ_LOG(3,(THIS_FILE, " %s", title)); + + /* Modify registrar settings */ + pj_memcpy(®istrar.cfg, srv_cfg, sizeof(*srv_cfg)); + + pj_bzero(&client_result, sizeof(client_result)); + client_result.destroy_on_cb = client_cfg->destroy_on_cb; + + status = pjsip_regc_create(endpt, &client_result, &client_cb, ®c); + if (status != PJ_SUCCESS) + return -100; + + status = pjsip_regc_init(regc, registrar_uri, &aor, &aor, contact_cnt, + contacts, expires ? expires : 60); + if (status != PJ_SUCCESS) { + pjsip_regc_destroy(regc); + return -110; + } + + if (client_cfg->auth) { + pjsip_cred_info cred; + + pj_bzero(&cred, sizeof(cred)); + cred.realm = pj_str("*"); + cred.scheme = pj_str("digest"); + cred.username = pj_str("user"); + cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + cred.data = pj_str("password"); + + status = pjsip_regc_set_credentials(regc, 1, &cred); + if (status != PJ_SUCCESS) { + pjsip_regc_destroy(regc); + return -115; + } + } + + /* Register */ + status = pjsip_regc_register(regc, PJ_TRUE, &tdata); + if (status != PJ_SUCCESS) { + pjsip_regc_destroy(regc); + return -120; + } + status = pjsip_regc_send(regc, tdata); + + /* That's it, wait until the callback is sent */ + for (i=0; i<600 && !client_result.done; ++i) { + flush_events(100); + } + + if (!client_result.done) { + PJ_LOG(3,(THIS_FILE, " error: test has timed out")); + pjsip_regc_destroy(regc); + return -200; + } + + /* Destroy the regc, we're done with the test, unless we're + * instructed to leave the session open. + */ + if (!leave_session && !client_cfg->destroy_on_cb) + pjsip_regc_destroy(regc); + + /* Compare results with expected results */ + + if (client_result.error != client_cfg->error) { + PJ_LOG(3,(THIS_FILE, " error: expecting err=%d, got err=%d", + client_cfg->error, client_result.error)); + return -210; + } + if (client_result.code != client_cfg->code) { + PJ_LOG(3,(THIS_FILE, " error: expecting code=%d, got code=%d", + client_cfg->code, client_result.code)); + return -220; + } + if (client_result.expiration != client_cfg->expiration) { + PJ_LOG(3,(THIS_FILE, " error: expecting expiration=%d, got expiration=%d", + client_cfg->expiration, client_result.expiration)); + return -240; + } + if (client_result.contact_cnt != client_cfg->contact_cnt) { + PJ_LOG(3,(THIS_FILE, " error: expecting contact_cnt=%d, got contact_cnt=%d", + client_cfg->contact_cnt, client_result.contact_cnt)); + return -250; + } + if (client_result.have_reg != client_cfg->have_reg) { + PJ_LOG(3,(THIS_FILE, " error: expecting have_reg=%d, got have_reg=%d", + client_cfg->have_reg, client_result.have_reg)); + return -260; + } + if (client_result.interval != client_result.expiration) { + PJ_LOG(3,(THIS_FILE, " error: interval (%d) is different than expiration (%d)", + client_result.interval, client_result.expiration)); + return -270; + } + if (client_result.expiration > 0 && client_result.next_reg < 1) { + PJ_LOG(3,(THIS_FILE, " error: next_reg=%d, expecting positive number because expiration is %d", + client_result.next_reg, client_result.expiration)); + return -280; + } + + /* Looks like everything is okay. */ + if (leave_session) { + *p_regc = regc; + } + + return 0; +} + + +/************************************************************************/ +/* Customized tests */ + +/* Check that client is sending register refresh */ +static int keep_alive_test(const pj_str_t *registrar_uri) +{ + enum { TIMEOUT = 40 }; + struct registrar_cfg server_cfg = + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, EXACT, TIMEOUT, 0, {NULL, 0}}; + struct client client_cfg = + /* error code have_reg expiration contact_cnt auth? destroy*/ + { PJ_FALSE, 200, PJ_TRUE, TIMEOUT, 1, PJ_FALSE,PJ_FALSE}; + pj_str_t contact = pj_str("<sip:c@C>"); + + + pjsip_regc *regc; + unsigned i; + int ret; + + ret = do_test("register refresh (takes ~40 secs)", &server_cfg, &client_cfg, registrar_uri, + 1, &contact, TIMEOUT, PJ_TRUE, ®c); + if (ret != 0) + return ret; + + /* Reset server response_cnt */ + registrar.response_cnt = 0; + + /* Wait until keep-alive/refresh is done */ + for (i=0; i<(TIMEOUT-1)*10 && registrar.response_cnt==0; ++i) { + flush_events(100); + } + + if (registrar.response_cnt==0) { + PJ_LOG(3,(THIS_FILE, " error: no refresh is received")); + return -400; + } + + if (client_result.error) { + PJ_LOG(3,(THIS_FILE, " error: got error")); + return -410; + } + if (client_result.code != 200) { + PJ_LOG(3,(THIS_FILE, " error: expecting code=%d, got code=%d", + 200, client_result.code)); + return -420; + } + if (client_result.expiration != TIMEOUT) { + PJ_LOG(3,(THIS_FILE, " error: expecting expiration=%d, got expiration=%d", + TIMEOUT, client_result.expiration)); + return -440; + } + if (client_result.contact_cnt != 1) { + PJ_LOG(3,(THIS_FILE, " error: expecting contact_cnt=%d, got contact_cnt=%d", + TIMEOUT, client_result.contact_cnt)); + return -450; + } + if (client_result.have_reg == 0) { + PJ_LOG(3,(THIS_FILE, " error: expecting have_reg=%d, got have_reg=%d", + 1, client_result.have_reg)); + return -460; + } + if (client_result.interval != TIMEOUT) { + PJ_LOG(3,(THIS_FILE, " error: interval (%d) is different than expiration (%d)", + client_result.interval, TIMEOUT)); + return -470; + } + if (client_result.expiration > 0 && client_result.next_reg < 1) { + PJ_LOG(3,(THIS_FILE, " error: next_reg=%d, expecting positive number because expiration is %d", + client_result.next_reg, client_result.expiration)); + return -480; + } + + /* Success */ + pjsip_regc_destroy(regc); + return 0; +} + + +/* Send error on refresh */ +static int refresh_error(const pj_str_t *registrar_uri, + pj_bool_t destroy_on_cb) +{ + enum { TIMEOUT = 40 }; + struct registrar_cfg server_cfg = + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, EXACT, TIMEOUT, 0, {NULL, 0}}; + struct client client_cfg = + /* error code have_reg expiration contact_cnt auth? destroy*/ + { PJ_FALSE, 200, PJ_TRUE, TIMEOUT, 1, PJ_FALSE,PJ_FALSE}; + pj_str_t contact = pj_str("<sip:c@C>"); + + pjsip_regc *regc; + unsigned i; + int ret; + + ret = do_test("refresh error (takes ~40 secs)", &server_cfg, &client_cfg, registrar_uri, + 1, &contact, TIMEOUT, PJ_TRUE, ®c); + if (ret != 0) + return ret; + + /* Reset server response_cnt */ + registrar.response_cnt = 0; + + /* inject error for transmission */ + send_mod.count = 0; + send_mod.count_before_reject = 0; + + /* reconfigure client */ + client_result.done = PJ_FALSE; + client_result.destroy_on_cb = destroy_on_cb; + + /* Wait until keep-alive/refresh is done */ + for (i=0; i<TIMEOUT*10 && !client_result.done; ++i) { + flush_events(100); + } + + send_mod.count_before_reject = 0xFFFF; + + if (!destroy_on_cb) + pjsip_regc_destroy(regc); + + if (!client_result.done) { + PJ_LOG(3,(THIS_FILE, " error: test has timed out")); + return -500; + } + + /* Expecting error */ + if (client_result.error==PJ_FALSE && client_result.code/100==2) { + PJ_LOG(3,(THIS_FILE, " error: expecting error got successfull result")); + return -510; + } + + return PJ_SUCCESS; +}; + + +/* Send error on refresh */ +static int update_test(const pj_str_t *registrar_uri) +{ + enum { TIMEOUT = 40 }; + struct registrar_cfg server_cfg = + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, EXACT, TIMEOUT, 0, {NULL, 0}}; + struct client client_cfg = + /* error code have_reg expiration contact_cnt auth? destroy*/ + { PJ_FALSE, 200, PJ_TRUE, TIMEOUT, 1, PJ_FALSE,PJ_FALSE}; + pj_str_t contacts[] = { + { "<sip:a>", 7 }, + { "<sip:b>", 7 }, + { "<sip:c>", 7 } + }; + + pjsip_regc *regc; + pjsip_contact_hdr *h1, *h2; + pjsip_sip_uri *u1, *u2; + unsigned i; + pj_status_t status; + pjsip_tx_data *tdata = NULL; + int ret = 0; + + /* initially only has 1 contact */ + ret = do_test("update test", &server_cfg, &client_cfg, registrar_uri, + 1, &contacts[0], TIMEOUT, PJ_TRUE, ®c); + if (ret != 0) { + return -600; + } + + /***** + * replace the contact with new one + */ + PJ_LOG(3,(THIS_FILE, " replacing contact")); + status = pjsip_regc_update_contact(regc, 1, &contacts[1]); + if (status != PJ_SUCCESS) { + ret = -610; + goto on_return; + } + + status = pjsip_regc_register(regc, PJ_TRUE, &tdata); + if (status != PJ_SUCCESS) { + ret = -620; + goto on_return; + } + + /* Check that the REGISTER contains two Contacts: + * - <sip:a>;expires=0, + * - <sip:b> + */ + h1 = (pjsip_contact_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL); + if (!h1) { + ret = -630; + goto on_return; + } + if ((void*)h1->next == (void*)&tdata->msg->hdr) + h2 = NULL; + else + h2 = (pjsip_contact_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h1->next); + if (!h2) { + ret = -640; + goto on_return; + } + /* must not have other Contact header */ + if ((void*)h2->next != (void*)&tdata->msg->hdr && + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h2->next) != NULL) + { + ret = -645; + goto on_return; + } + + u1 = (pjsip_sip_uri*) pjsip_uri_get_uri(h1->uri); + u2 = (pjsip_sip_uri*) pjsip_uri_get_uri(h2->uri); + + if (*u1->host.ptr == 'a') { + if (h1->expires != 0) { + ret = -650; + goto on_return; + } + if (h2->expires == 0) { + ret = -660; + goto on_return; + } + + } else { + pj_assert(*u1->host.ptr == 'b'); + if (h1->expires == 0) { + ret = -670; + goto on_return; + } + if (h2->expires != 0) { + ret = -680; + goto on_return; + } + } + + /* Destroy tdata */ + pjsip_tx_data_dec_ref(tdata); + tdata = NULL; + + + + /** + * First loop, it will update with more contacts. Second loop + * should do nothing. + */ + for (i=0; i<2; ++i) { + if (i==0) + PJ_LOG(3,(THIS_FILE, " replacing with more contacts")); + else + PJ_LOG(3,(THIS_FILE, " updating contacts with same contacts")); + + status = pjsip_regc_update_contact(regc, 2, &contacts[1]); + if (status != PJ_SUCCESS) { + ret = -710; + goto on_return; + } + + status = pjsip_regc_register(regc, PJ_TRUE, &tdata); + if (status != PJ_SUCCESS) { + ret = -720; + goto on_return; + } + + /* Check that the REGISTER contains two Contacts: + * - <sip:b> + * - <sip:c> + */ + h1 = (pjsip_contact_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL); + if (!h1) { + ret = -730; + goto on_return; + } + if ((void*)h1->next == (void*)&tdata->msg->hdr) + h2 = NULL; + else + h2 = (pjsip_contact_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h1->next); + if (!h2) { + ret = -740; + goto on_return; + } + /* must not have other Contact header */ + if ((void*)h2->next != (void*)&tdata->msg->hdr && + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h2->next) != NULL) + { + ret = -745; + goto on_return; + } + + /* both contacts must not have expires=0 parameter */ + if (h1->expires == 0) { + ret = -750; + goto on_return; + } + if (h2->expires == 0) { + ret = -760; + goto on_return; + } + + /* Destroy tdata */ + pjsip_tx_data_dec_ref(tdata); + tdata = NULL; + } + +on_return: + if (tdata) pjsip_tx_data_dec_ref(tdata); + pjsip_regc_destroy(regc); + return ret; +}; + + +/* send error on authentication */ +static int auth_send_error(const pj_str_t *registrar_uri, + pj_bool_t destroy_on_cb) +{ + enum { TIMEOUT = 40 }; + struct registrar_cfg server_cfg = + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_TRUE, EXACT, 75, 0, {NULL, 0}}; + struct client client_cfg = + /* error code have_reg expiration contact_cnt auth? destroy*/ + { PJ_TRUE, 401, PJ_FALSE, -1, 0, PJ_TRUE, PJ_TRUE}; + pj_str_t contact = pj_str("<sip:c@C>"); + + pjsip_regc *regc; + int ret; + + client_cfg.destroy_on_cb = destroy_on_cb; + + /* inject error for second request retry */ + send_mod.count = 0; + send_mod.count_before_reject = 1; + + ret = do_test("auth send error", &server_cfg, &client_cfg, registrar_uri, + 1, &contact, TIMEOUT, PJ_TRUE, ®c); + + send_mod.count_before_reject = 0xFFFF; + + return ret; +}; + + + + +/************************************************************************/ +enum +{ + OFF = 1, + ON = 2, + ON_OFF = 3, +}; + +int regc_test(void) +{ + struct test_rec { + unsigned check_contact; + unsigned add_xuid_param; + + const char *title; + char *alt_registrar; + unsigned contact_cnt; + char *contacts[4]; + unsigned expires; + struct registrar_cfg server_cfg; + struct client client_cfg; + } test_rec[] = + { + /* immediate error */ + { + OFF, /* check_contact */ + OFF, /* add_xuid_param */ + "immediate error", /* title */ + "sip:unresolved-host-xyy", /* alt_registrar */ + 1, /* contact cnt */ + { "sip:user@127.0.0.1:5060" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_FALSE, 200, PJ_FALSE, NONE, 0, 0, {NULL, 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 503, PJ_FALSE, -1, 0, PJ_FALSE} + }, + + /* timeout test */ + { + OFF, /* check_contact */ + OFF, /* add_xuid_param */ + "timeout test (takes ~32 secs)",/* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "sip:user@127.0.0.1:5060" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_FALSE, 200, PJ_FALSE, NONE, 0, 0, {NULL, 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth? */ + { PJ_FALSE, 408, PJ_FALSE, -1, 0, PJ_FALSE} + }, + + /* Basic successful registration scenario: + * a good registrar returns the Contact header as is and + * add expires parameter. In this test no additional bindings + * are returned. + */ + { + ON_OFF, /* check_contact */ + ON_OFF, /* add_xuid_param */ + "basic", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060;transport=udp;x-param=1234>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, EXACT, 75, 65, {NULL, 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 75, 1, PJ_FALSE} + }, + + /* Basic successful registration scenario with authentication + */ + { + ON_OFF, /* check_contact */ + ON_OFF, /* add_xuid_param */ + "authentication", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060;transport=udp;x-param=1234>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_TRUE, EXACT, 75, 65, {NULL, 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 75, 1, PJ_TRUE} + }, + + /* a good registrar returns the Contact header as is and + * add expires parameter. Also it adds bindings from other + * clients in this test. + */ + { + ON_OFF, /* check_contact */ + ON, /* add_xuid_param */ + "more bindings in response", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060;transport=udp>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, EXACT, 75, 65, {"<sip:a@a>;expires=70", 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 75, 2, PJ_FALSE} + }, + + + /* a bad registrar returns modified Contact header, but it + * still returns all parameters intact. In this case + * the expiration is taken from the expires param because + * of matching xuid param or because the number of + * Contact header matches. + */ + { + ON_OFF, /* check_contact */ + ON_OFF, /* add_xuid_param */ + "registrar modifies Contact header", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, MODIFIED, 75, 65, {NULL, 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 75, 1, PJ_FALSE} + }, + + + /* a bad registrar returns modified Contact header, but it + * still returns all parameters intact. In addition it returns + * bindings from other clients. + * + * In this case the expiration is taken from the expires param + * because add_xuid_param is enabled. + */ + { + ON_OFF, /* check_contact */ + ON, /* add_xuid_param */ + "registrar modifies Contact header and add bindings", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, MODIFIED, 75, 65, {"<sip:a@a>;expires=70", 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 75, 2, PJ_FALSE} + }, + + + /* a bad registrar returns completely different Contact and + * all parameters are gone. In this case the expiration is + * also taken from the expires param since the number of + * header matches. + */ + { + ON_OFF, /* check_contact */ + ON_OFF, /* add_xuid_param */ + "registrar replaces Contact header", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 202, PJ_FALSE, NONE, 0, 65, {"<sip:a@A>;expires=75", 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 202, PJ_TRUE, 75, 1, PJ_FALSE} + }, + + + /* a bad registrar returns completely different Contact (and + * all parameters are gone) and it also includes bindings from + * other clients. + * In this case the expiration is taken from the Expires header. + */ + { + ON_OFF, /* check_contact */ + ON_OFF, /* add_xuid_param */ + " as above with additional bindings", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, NONE, 0, 65, {"<sip:a@A>;expires=75, <sip:b@B;expires=70>", 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 65, 2, PJ_FALSE} + }, + + /* the registrar doesn't return any bindings, but for some + * reason it includes an Expires header. + * In this case the expiration is taken from the Expires header. + */ + { + ON_OFF, /* check_contact */ + ON_OFF, /* add_xuid_param */ + "no Contact but with Expires", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, NONE, 0, 65, {NULL, 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 65, 0, PJ_FALSE} + }, + + /* Neither Contact header nor Expires header are present. + * In this case the expiration is taken from the request. + */ + { + ON_OFF, /* check_contact */ + ON_OFF, /* add_xuid_param */ + "no Contact and no Expires", /* title */ + NULL, /* alt_registrar */ + 1, /* contact cnt */ + { "<sip:user@127.0.0.1:5060>" },/* contacts[] */ + 600, /* expires */ + + /* registrar config: */ + /* respond code auth contact exp_prm expires more_contacts */ + { PJ_TRUE, 200, PJ_FALSE, NONE, 0, 0, {NULL, 0}}, + + /* client expected results: */ + /* error code have_reg expiration contact_cnt auth?*/ + { PJ_FALSE, 200, PJ_TRUE, 600, 0, PJ_FALSE} + }, + }; + + unsigned i; + pj_sockaddr_in addr; + pjsip_transport *udp = NULL; + pj_uint16_t port; + char registrar_uri_buf[80]; + pj_str_t registrar_uri; + int rc = 0; + + pj_sockaddr_in_init(&addr, 0, 0); + + /* Acquire existing transport, if any */ + rc = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &addr, sizeof(addr), NULL, &udp); + if (rc == PJ_SUCCESS) { + port = pj_sockaddr_get_port(&udp->local_addr); + pjsip_transport_dec_ref(udp); + udp = NULL; + } else { + rc = pjsip_udp_transport_start(endpt, NULL, NULL, 1, &udp); + if (rc != PJ_SUCCESS) { + app_perror(" error creating UDP transport", rc); + rc = -2; + goto on_return; + } + + port = pj_sockaddr_get_port(&udp->local_addr); + } + + /* Register registrar module */ + rc = pjsip_endpt_register_module(endpt, ®istrar.mod); + if (rc != PJ_SUCCESS) { + app_perror(" error registering module", rc); + rc = -3; + goto on_return; + } + + /* Register send module */ + rc = pjsip_endpt_register_module(endpt, &send_mod.mod); + if (rc != PJ_SUCCESS) { + app_perror(" error registering module", rc); + rc = -3; + goto on_return; + } + + pj_ansi_snprintf(registrar_uri_buf, sizeof(registrar_uri_buf), + "sip:127.0.0.1:%d", (int)port); + registrar_uri = pj_str(registrar_uri_buf); + + for (i=0; i<PJ_ARRAY_SIZE(test_rec); ++i) { + struct test_rec *t = &test_rec[i]; + unsigned j, x; + pj_str_t reg_uri; + pj_str_t contacts[8]; + + /* Fill in the registrar address if it's not specified */ + if (t->alt_registrar == NULL) { + reg_uri = registrar_uri; + } else { + reg_uri = pj_str(t->alt_registrar); + } + + /* Build contact pj_str_t's */ + for (j=0; j<t->contact_cnt; ++j) { + contacts[j] = pj_str(t->contacts[j]); + } + + /* Normalize more_contacts field */ + if (t->server_cfg.more_contacts.ptr) + t->server_cfg.more_contacts.slen = strlen(t->server_cfg.more_contacts.ptr); + + /* Do tests with three combinations: + * - check_contact on/off + * - add_xuid_param on/off + * - destroy_on_callback on/off + */ + for (x=1; x<=2; ++x) { + unsigned y; + + if ((t->check_contact & x) == 0) + continue; + + pjsip_cfg()->regc.check_contact = (x-1); + + for (y=1; y<=2; ++y) { + unsigned z; + + if ((t->add_xuid_param & y) == 0) + continue; + + pjsip_cfg()->regc.add_xuid_param = (y-1); + + for (z=0; z<=1; ++z) { + char new_title[200]; + + t->client_cfg.destroy_on_cb = z; + + sprintf(new_title, "%s [check=%d, xuid=%d, destroy=%d]", + t->title, pjsip_cfg()->regc.check_contact, + pjsip_cfg()->regc.add_xuid_param, z); + rc = do_test(new_title, &t->server_cfg, &t->client_cfg, + ®_uri, t->contact_cnt, contacts, + t->expires, PJ_FALSE, NULL); + if (rc != 0) + goto on_return; + } + + } + } + + /* Sleep between test groups to avoid using up too many + * active transactions. + */ + pj_thread_sleep(1000); + } + + /* keep-alive test */ + rc = keep_alive_test(®istrar_uri); + if (rc != 0) + goto on_return; + + /* Send error on refresh without destroy on callback */ + rc = refresh_error(®istrar_uri, PJ_FALSE); + if (rc != 0) + goto on_return; + + /* Send error on refresh, destroy on callback */ + rc = refresh_error(®istrar_uri, PJ_TRUE); + if (rc != 0) + goto on_return; + + /* Updating contact */ + rc = update_test(®istrar_uri); + if (rc != 0) + goto on_return; + + /* Send error during auth, don't destroy on callback */ + rc = auth_send_error(®istrar_uri, PJ_FALSE); + if (rc != 0) + goto on_return; + + /* Send error during auth, destroy on callback */ + rc = auth_send_error(®istrar_uri, PJ_FALSE); + if (rc != 0) + goto on_return; + +on_return: + if (registrar.mod.id != -1) { + pjsip_endpt_unregister_module(endpt, ®istrar.mod); + } + if (send_mod.mod.id != -1) { + pjsip_endpt_unregister_module(endpt, &send_mod.mod); + } + if (udp) { + pjsip_transport_dec_ref(udp); + } + return rc; +} + + |