config_parser.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2010, Digium, Inc.
  5. *
  6. * See http://www.asterisk.org for more information about
  7. * the Asterisk project. Please do not directly contact
  8. * any of the maintainers of this project for assistance;
  9. * the project provides a web site, mailing lists and IRC
  10. * channels for your use.
  11. *
  12. * This program is free software, distributed under the terms of
  13. * the GNU General Public License Version 2. See the LICENSE file
  14. * at the top of the source tree.
  15. */
  16. /*!
  17. * \file
  18. * \brief sip config parsing functions and unit tests
  19. */
  20. /*** MODULEINFO
  21. <support_level>extended</support_level>
  22. ***/
  23. #include "asterisk.h"
  24. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  25. #include "include/sip.h"
  26. #include "include/config_parser.h"
  27. #include "include/sip_utils.h"
  28. /*! \brief Parse register=> line in sip.conf
  29. *
  30. * \retval 0 on success
  31. * \retval -1 on failure
  32. */
  33. int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const char *value, int lineno)
  34. {
  35. int portnum = 0;
  36. int domainport = 0;
  37. enum ast_transport transport = AST_TRANSPORT_UDP;
  38. char buf[256] = "";
  39. char *userpart = NULL, *hostpart = NULL;
  40. /* register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] */
  41. AST_DECLARE_APP_ARGS(pre1,
  42. AST_APP_ARG(peer);
  43. AST_APP_ARG(userpart);
  44. );
  45. AST_DECLARE_APP_ARGS(pre2,
  46. AST_APP_ARG(transport);
  47. AST_APP_ARG(blank);
  48. AST_APP_ARG(userpart);
  49. );
  50. AST_DECLARE_APP_ARGS(user1,
  51. AST_APP_ARG(userpart);
  52. AST_APP_ARG(secret);
  53. AST_APP_ARG(authuser);
  54. );
  55. AST_DECLARE_APP_ARGS(user2,
  56. AST_APP_ARG(user);
  57. AST_APP_ARG(domain);
  58. );
  59. AST_DECLARE_APP_ARGS(user3,
  60. AST_APP_ARG(authuser);
  61. AST_APP_ARG(domainport);
  62. );
  63. AST_DECLARE_APP_ARGS(host1,
  64. AST_APP_ARG(hostpart);
  65. AST_APP_ARG(expiry);
  66. );
  67. AST_DECLARE_APP_ARGS(host2,
  68. AST_APP_ARG(hostpart);
  69. AST_APP_ARG(extension);
  70. );
  71. AST_DECLARE_APP_ARGS(host3,
  72. AST_APP_ARG(host);
  73. AST_APP_ARG(port);
  74. );
  75. if (!reg) {
  76. return -1;
  77. }
  78. reg->expire = -1;
  79. reg->timeout = -1;
  80. if (!value) {
  81. return -1;
  82. }
  83. ast_copy_string(buf, value, sizeof(buf));
  84. /*! register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry]
  85. * becomes
  86. * userpart => [peer?][transport://]user[@domain][:secret[:authuser]]
  87. * hostpart => host[:port][/extension][~expiry]
  88. */
  89. if ((hostpart = strrchr(buf, '@'))) {
  90. *hostpart++ = '\0';
  91. userpart = buf;
  92. }
  93. if (ast_strlen_zero(userpart) || ast_strlen_zero(hostpart)) {
  94. ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno);
  95. return -1;
  96. }
  97. /*!
  98. * pre1.peer => peer
  99. * pre1.userpart => [transport://]user[@domain][:secret[:authuser]]
  100. * hostpart => host[:port][/extension][~expiry]
  101. */
  102. AST_NONSTANDARD_RAW_ARGS(pre1, userpart, '?');
  103. if (ast_strlen_zero(pre1.userpart)) {
  104. pre1.userpart = pre1.peer;
  105. pre1.peer = NULL;
  106. }
  107. /*!
  108. * pre1.peer => peer
  109. * pre2.transport = transport
  110. * pre2.userpart => user[@domain][:secret[:authuser]]
  111. * hostpart => host[:port][/extension][~expiry]
  112. */
  113. AST_NONSTANDARD_RAW_ARGS(pre2, pre1.userpart, '/');
  114. if (ast_strlen_zero(pre2.userpart)) {
  115. pre2.userpart = pre2.transport;
  116. pre2.transport = NULL;
  117. } else {
  118. pre2.transport[strlen(pre2.transport) - 1] = '\0'; /* Remove trailing : */
  119. }
  120. if (!ast_strlen_zero(pre2.blank)) {
  121. ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno);
  122. return -1;
  123. }
  124. /*!
  125. * pre1.peer => peer
  126. * pre2.transport = transport
  127. * user1.userpart => user[@domain]
  128. * user1.secret => secret
  129. * user1.authuser => authuser
  130. * hostpart => host[:port][/extension][~expiry]
  131. */
  132. AST_NONSTANDARD_RAW_ARGS(user1, pre2.userpart, ':');
  133. /*!
  134. * pre1.peer => peer
  135. * pre2.transport = transport
  136. * user1.userpart => user[@domain]
  137. * user1.secret => secret
  138. * user1.authuser => authuser
  139. * host1.hostpart => host[:port][/extension]
  140. * host1.expiry => [expiry]
  141. */
  142. AST_NONSTANDARD_RAW_ARGS(host1, hostpart, '~');
  143. /*!
  144. * pre1.peer => peer
  145. * pre2.transport = transport
  146. * user1.userpart => user[@domain]
  147. * user1.secret => secret
  148. * user1.authuser => authuser
  149. * host2.hostpart => host[:port]
  150. * host2.extension => [extension]
  151. * host1.expiry => [expiry]
  152. */
  153. AST_NONSTANDARD_RAW_ARGS(host2, host1.hostpart, '/');
  154. /*!
  155. * pre1.peer => peer
  156. * pre2.transport = transport
  157. * user1.userpart => user[@domain]
  158. * user1.secret => secret
  159. * user1.authuser => authuser
  160. * host3.host => host
  161. * host3.port => port
  162. * host2.extension => extension
  163. * host1.expiry => expiry
  164. */
  165. AST_NONSTANDARD_RAW_ARGS(host3, host2.hostpart, ':');
  166. /*!
  167. * pre1.peer => peer
  168. * pre2.transport = transport
  169. * user2.user => user
  170. * user2.domain => domain
  171. * user1.secret => secret
  172. * user1.authuser => authuser
  173. * host3.host => host
  174. * host3.port => port
  175. * host2.extension => extension
  176. * host1.expiry => expiry
  177. */
  178. AST_NONSTANDARD_RAW_ARGS(user2, user1.userpart, '@');
  179. /*!
  180. * pre1.peer => peer
  181. * pre2.transport = transport
  182. * user2.user => user
  183. * user2.domain => domain
  184. * user1.secret => secret
  185. * user3.authuser => authuser
  186. * user3.domainport => domainport
  187. * host3.host => host
  188. * host3.port => port
  189. * host2.extension => extension
  190. * host1.expiry => expiry
  191. */
  192. AST_NONSTANDARD_RAW_ARGS(user3, user1.authuser, ':');
  193. /* Reordering needed due to fields being [(:secret[:username])|(:regdomainport:secret:username)]
  194. but parsing being [secret[:username[:regdomainport]]] */
  195. if (user3.argc == 2) {
  196. char *reorder = user3.domainport;
  197. user3.domainport = user1.secret;
  198. user1.secret = user3.authuser;
  199. user3.authuser = reorder;
  200. }
  201. if (host3.port) {
  202. if (!(portnum = port_str2int(host3.port, 0))) {
  203. ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", host3.port, lineno);
  204. }
  205. }
  206. if (user3.domainport) {
  207. if (!(domainport = port_str2int(user3.domainport, 0))) {
  208. ast_log(LOG_NOTICE, "'%s' is not a valid domain port number on line %d of sip.conf. using default.\n", user3.domainport, lineno);
  209. }
  210. }
  211. /* set transport type */
  212. if (!pre2.transport) {
  213. transport = AST_TRANSPORT_UDP;
  214. } else if (!strncasecmp(pre2.transport, "tcp", 3)) {
  215. transport = AST_TRANSPORT_TCP;
  216. } else if (!strncasecmp(pre2.transport, "tls", 3)) {
  217. transport = AST_TRANSPORT_TLS;
  218. } else if (!strncasecmp(pre2.transport, "udp", 3)) {
  219. transport = AST_TRANSPORT_UDP;
  220. } else {
  221. transport = AST_TRANSPORT_UDP;
  222. ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", pre2.transport, lineno);
  223. }
  224. /* if no portnum specified, set default for transport */
  225. if (!portnum) {
  226. if (transport == AST_TRANSPORT_TLS) {
  227. portnum = STANDARD_TLS_PORT;
  228. } else {
  229. portnum = STANDARD_SIP_PORT;
  230. }
  231. }
  232. /* copy into sip_registry object */
  233. ast_string_field_set(reg, callback, ast_strip_quoted(S_OR(host2.extension, "s"), "\"", "\""));
  234. ast_string_field_set(reg, username, ast_strip_quoted(S_OR(user2.user, ""), "\"", "\""));
  235. ast_string_field_set(reg, hostname, ast_strip_quoted(S_OR(host3.host, ""), "\"", "\""));
  236. ast_string_field_set(reg, authuser, ast_strip_quoted(S_OR(user3.authuser, ""), "\"", "\""));
  237. ast_string_field_set(reg, secret, ast_strip_quoted(S_OR(user1.secret, ""), "\"", "\""));
  238. ast_string_field_set(reg, peername, ast_strip_quoted(S_OR(pre1.peer, ""), "\"", "\""));
  239. ast_string_field_set(reg, regdomain, ast_strip_quoted(S_OR(user2.domain, ""), "\"", "\""));
  240. reg->transport = transport;
  241. reg->portno = portnum;
  242. reg->regdomainport = domainport;
  243. reg->callid_valid = FALSE;
  244. reg->ocseq = INITIAL_CSEQ;
  245. reg->refresh = reg->expiry = reg->configured_expiry = (host1.expiry ? atoi(ast_strip_quoted(host1.expiry, "\"", "\"")) : default_expiry);
  246. return 0;
  247. }
  248. #ifdef TEST_FRAMEWORK
  249. AST_TEST_DEFINE(sip_parse_register_line_test)
  250. {
  251. int res = AST_TEST_PASS;
  252. struct sip_registry *reg;
  253. int default_expiry = 120;
  254. const char *reg1 = "name@domain";
  255. const char *reg2 = "name:pass@domain";
  256. const char *reg3 = "name@namedomain:pass:authuser@domain";
  257. const char *reg4 = "name@namedomain:pass:authuser@domain/extension";
  258. const char *reg5 = "tcp://name@namedomain:pass:authuser@domain/extension";
  259. const char *reg6 = "tls://name@namedomain:pass:authuser@domain/extension~111";
  260. const char *reg7 = "peer?tcp://name@namedomain:pass:authuser@domain:1234/extension~111";
  261. const char *reg8 = "peer?name@namedomain:pass:authuser@domain:1234/extension~111";
  262. const char *reg9 = "peer?name:pass:authuser:1234/extension~111";
  263. const char *reg10 = "@domin:1234";
  264. const char *reg12 = "name@namedomain:4321:pass:authuser@domain";
  265. const char *reg13 = "name@namedomain:4321::@domain";
  266. switch (cmd) {
  267. case TEST_INIT:
  268. info->name = "sip_parse_register_line_test";
  269. info->category = "/channels/chan_sip/";
  270. info->summary = "tests sip register line parsing";
  271. info->description =
  272. "Tests parsing of various register line configurations. "
  273. "Verifies output matches expected behavior.";
  274. return AST_TEST_NOT_RUN;
  275. case TEST_EXECUTE:
  276. break;
  277. }
  278. /* ---Test reg 1, simple config --- */
  279. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  280. goto alloc_fail;
  281. } else if (
  282. sip_parse_register_line(reg, default_expiry, reg1, 1) ||
  283. strcmp(reg->callback, "s") ||
  284. strcmp(reg->username, "name") ||
  285. strcmp(reg->regdomain, "") ||
  286. strcmp(reg->hostname, "domain") ||
  287. strcmp(reg->authuser, "") ||
  288. strcmp(reg->secret, "") ||
  289. strcmp(reg->peername, "") ||
  290. reg->transport != AST_TRANSPORT_UDP ||
  291. reg->timeout != -1 ||
  292. reg->expire != -1 ||
  293. reg->refresh != default_expiry ||
  294. reg->expiry != default_expiry ||
  295. reg->configured_expiry != default_expiry ||
  296. reg->portno != STANDARD_SIP_PORT ||
  297. (reg->regdomainport) ||
  298. reg->callid_valid != FALSE ||
  299. reg->ocseq != INITIAL_CSEQ) {
  300. ast_test_status_update(test, "Test 1: simple config failed\n");
  301. res = AST_TEST_FAIL;
  302. }
  303. ast_string_field_free_memory(reg);
  304. ast_free(reg);
  305. /* ---Test reg 2, add secret --- */
  306. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  307. goto alloc_fail;
  308. } else if (
  309. sip_parse_register_line(reg, default_expiry, reg2, 1) ||
  310. strcmp(reg->callback, "s") ||
  311. strcmp(reg->username, "name") ||
  312. strcmp(reg->regdomain, "") ||
  313. strcmp(reg->hostname, "domain") ||
  314. strcmp(reg->authuser, "") ||
  315. strcmp(reg->secret, "pass") ||
  316. strcmp(reg->peername, "") ||
  317. reg->transport != AST_TRANSPORT_UDP ||
  318. reg->timeout != -1 ||
  319. reg->expire != -1 ||
  320. reg->refresh != default_expiry ||
  321. reg->expiry != default_expiry ||
  322. reg->configured_expiry != default_expiry ||
  323. reg->portno != STANDARD_SIP_PORT ||
  324. (reg->regdomainport) ||
  325. reg->callid_valid != FALSE ||
  326. reg->ocseq != INITIAL_CSEQ) {
  327. ast_test_status_update(test, "Test 2: add secret failed\n");
  328. res = AST_TEST_FAIL;
  329. }
  330. ast_string_field_free_memory(reg);
  331. ast_free(reg);
  332. /* ---Test reg 3, add userdomain and authuser --- */
  333. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  334. goto alloc_fail;
  335. } else if (
  336. sip_parse_register_line(reg, default_expiry, reg3, 1) ||
  337. strcmp(reg->callback, "s") ||
  338. strcmp(reg->username, "name") ||
  339. strcmp(reg->regdomain, "namedomain") ||
  340. strcmp(reg->hostname, "domain") ||
  341. strcmp(reg->authuser, "authuser") ||
  342. strcmp(reg->secret, "pass") ||
  343. strcmp(reg->peername, "") ||
  344. reg->transport != AST_TRANSPORT_UDP ||
  345. reg->timeout != -1 ||
  346. reg->expire != -1 ||
  347. reg->refresh != default_expiry ||
  348. reg->expiry != default_expiry ||
  349. reg->configured_expiry != default_expiry ||
  350. reg->portno != STANDARD_SIP_PORT ||
  351. (reg->regdomainport) ||
  352. reg->callid_valid != FALSE ||
  353. reg->ocseq != INITIAL_CSEQ) {
  354. ast_test_status_update(test, "Test 3: add userdomain and authuser failed\n");
  355. res = AST_TEST_FAIL;
  356. }
  357. ast_string_field_free_memory(reg);
  358. ast_free(reg);
  359. /* ---Test reg 4, add callback extension --- */
  360. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  361. goto alloc_fail;
  362. } else if (
  363. sip_parse_register_line(reg, default_expiry, reg4, 1) ||
  364. strcmp(reg->callback, "extension") ||
  365. strcmp(reg->username, "name") ||
  366. strcmp(reg->regdomain, "namedomain") ||
  367. strcmp(reg->hostname, "domain") ||
  368. strcmp(reg->authuser, "authuser") ||
  369. strcmp(reg->secret, "pass") ||
  370. strcmp(reg->peername, "") ||
  371. reg->transport != AST_TRANSPORT_UDP ||
  372. reg->timeout != -1 ||
  373. reg->expire != -1 ||
  374. reg->refresh != default_expiry ||
  375. reg->expiry != default_expiry ||
  376. reg->configured_expiry != default_expiry ||
  377. reg->portno != STANDARD_SIP_PORT ||
  378. (reg->regdomainport) ||
  379. reg->callid_valid != FALSE ||
  380. reg->ocseq != INITIAL_CSEQ) {
  381. ast_test_status_update(test, "Test 4: add callback extension failed\n");
  382. res = AST_TEST_FAIL;
  383. }
  384. ast_string_field_free_memory(reg);
  385. ast_free(reg);
  386. /* ---Test reg 5, add transport --- */
  387. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  388. goto alloc_fail;
  389. } else if (
  390. sip_parse_register_line(reg, default_expiry, reg5, 1) ||
  391. strcmp(reg->callback, "extension") ||
  392. strcmp(reg->username, "name") ||
  393. strcmp(reg->regdomain, "namedomain") ||
  394. strcmp(reg->hostname, "domain") ||
  395. strcmp(reg->authuser, "authuser") ||
  396. strcmp(reg->secret, "pass") ||
  397. strcmp(reg->peername, "") ||
  398. reg->transport != AST_TRANSPORT_TCP ||
  399. reg->timeout != -1 ||
  400. reg->expire != -1 ||
  401. reg->refresh != default_expiry ||
  402. reg->expiry != default_expiry ||
  403. reg->configured_expiry != default_expiry ||
  404. reg->portno != STANDARD_SIP_PORT ||
  405. (reg->regdomainport) ||
  406. reg->callid_valid != FALSE ||
  407. reg->ocseq != INITIAL_CSEQ) {
  408. ast_test_status_update(test, "Test 5: add transport failed\n");
  409. res = AST_TEST_FAIL;
  410. }
  411. ast_string_field_free_memory(reg);
  412. ast_free(reg);
  413. /* ---Test reg 6, change to tls transport, add expiry --- */
  414. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  415. goto alloc_fail;
  416. } else if (
  417. sip_parse_register_line(reg, default_expiry, reg6, 1) ||
  418. strcmp(reg->callback, "extension") ||
  419. strcmp(reg->username, "name") ||
  420. strcmp(reg->regdomain, "namedomain") ||
  421. strcmp(reg->hostname, "domain") ||
  422. strcmp(reg->authuser, "authuser") ||
  423. strcmp(reg->secret, "pass") ||
  424. strcmp(reg->peername, "") ||
  425. reg->transport != AST_TRANSPORT_TLS ||
  426. reg->timeout != -1 ||
  427. reg->expire != -1 ||
  428. reg->refresh != 111 ||
  429. reg->expiry != 111 ||
  430. reg->configured_expiry != 111 ||
  431. reg->portno != STANDARD_TLS_PORT ||
  432. (reg->regdomainport) ||
  433. reg->callid_valid != FALSE ||
  434. reg->ocseq != INITIAL_CSEQ) {
  435. ast_test_status_update(test, "Test 6: change to tls transport and add expiry failed\n");
  436. res = AST_TEST_FAIL;
  437. }
  438. ast_string_field_free_memory(reg);
  439. ast_free(reg);
  440. /* ---Test reg 7, change transport to tcp, add custom port, and add peer --- */
  441. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  442. goto alloc_fail;
  443. } else if (
  444. sip_parse_register_line(reg, default_expiry, reg7, 1) ||
  445. strcmp(reg->callback, "extension") ||
  446. strcmp(reg->username, "name") ||
  447. strcmp(reg->regdomain, "namedomain") ||
  448. strcmp(reg->hostname, "domain") ||
  449. strcmp(reg->authuser, "authuser") ||
  450. strcmp(reg->secret, "pass") ||
  451. strcmp(reg->peername, "peer") ||
  452. reg->transport != AST_TRANSPORT_TCP ||
  453. reg->timeout != -1 ||
  454. reg->expire != -1 ||
  455. reg->refresh != 111 ||
  456. reg->expiry != 111 ||
  457. reg->configured_expiry != 111 ||
  458. reg->portno != 1234 ||
  459. (reg->regdomainport) ||
  460. reg->callid_valid != FALSE ||
  461. reg->ocseq != INITIAL_CSEQ) {
  462. ast_test_status_update(test, "Test 7, change transport to tcp, add custom port, and add peer failed.\n");
  463. res = AST_TEST_FAIL;
  464. }
  465. ast_string_field_free_memory(reg);
  466. ast_free(reg);
  467. /* ---Test reg 8, remove transport --- */
  468. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  469. goto alloc_fail;
  470. } else if (
  471. sip_parse_register_line(reg, default_expiry, reg8, 1) ||
  472. strcmp(reg->callback, "extension") ||
  473. strcmp(reg->username, "name") ||
  474. strcmp(reg->regdomain, "namedomain") ||
  475. strcmp(reg->hostname, "domain") ||
  476. strcmp(reg->authuser, "authuser") ||
  477. strcmp(reg->secret, "pass") ||
  478. strcmp(reg->peername, "peer") ||
  479. reg->transport != AST_TRANSPORT_UDP ||
  480. reg->timeout != -1 ||
  481. reg->expire != -1 ||
  482. reg->refresh != 111 ||
  483. reg->expiry != 111 ||
  484. reg->configured_expiry != 111 ||
  485. reg->portno != 1234 ||
  486. (reg->regdomainport) ||
  487. reg->callid_valid != FALSE ||
  488. reg->ocseq != INITIAL_CSEQ) {
  489. ast_test_status_update(test, "Test 8, remove transport failed.\n");
  490. res = AST_TEST_FAIL;
  491. }
  492. ast_string_field_free_memory(reg);
  493. ast_free(reg);
  494. /* ---Test reg 9, missing domain, expected to fail --- */
  495. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  496. goto alloc_fail;
  497. } else if (!sip_parse_register_line(reg, default_expiry, reg9, 1)) {
  498. ast_test_status_update(test,
  499. "Test 9, missing domain, expected to fail but did not.\n");
  500. res = AST_TEST_FAIL;
  501. }
  502. ast_string_field_free_memory(reg);
  503. ast_free(reg);
  504. /* ---Test reg 10, missing user, expected to fail --- */
  505. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  506. goto alloc_fail;
  507. } else if (!sip_parse_register_line(reg, default_expiry, reg10, 1)) {
  508. ast_test_status_update(test,
  509. "Test 10, missing user expected to fail but did not\n");
  510. res = AST_TEST_FAIL;
  511. }
  512. ast_string_field_free_memory(reg);
  513. ast_free(reg);
  514. /* ---Test reg 11, no registry object, expected to fail--- */
  515. if (!sip_parse_register_line(NULL, default_expiry, reg1, 1)) {
  516. ast_test_status_update(test,
  517. "Test 11, no registry object, expected to fail but did not.\n");
  518. res = AST_TEST_FAIL;
  519. }
  520. /* ---Test reg 12, no registry line, expected to fail --- */
  521. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  522. goto alloc_fail;
  523. } else if (!sip_parse_register_line(reg, default_expiry, NULL, 1)) {
  524. ast_test_status_update(test,
  525. "Test 12, NULL register line expected to fail but did not.\n");
  526. res = AST_TEST_FAIL;
  527. }
  528. ast_string_field_free_memory(reg);
  529. ast_free(reg);
  530. /* ---Test reg13, add domain port --- */
  531. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  532. goto alloc_fail;
  533. } else if (
  534. sip_parse_register_line(reg, default_expiry, reg12, 1) ||
  535. strcmp(reg->callback, "s") ||
  536. strcmp(reg->username, "name") ||
  537. strcmp(reg->regdomain, "namedomain") ||
  538. strcmp(reg->hostname, "domain") ||
  539. strcmp(reg->authuser, "authuser") ||
  540. strcmp(reg->secret, "pass") ||
  541. strcmp(reg->peername, "") ||
  542. reg->transport != AST_TRANSPORT_UDP ||
  543. reg->timeout != -1 ||
  544. reg->expire != -1 ||
  545. reg->refresh != default_expiry ||
  546. reg->expiry != default_expiry ||
  547. reg->configured_expiry != default_expiry ||
  548. reg->portno != STANDARD_SIP_PORT ||
  549. reg->regdomainport != 4321 ||
  550. reg->callid_valid != FALSE ||
  551. reg->ocseq != INITIAL_CSEQ) {
  552. ast_test_status_update(test, "Test 13, add domain port failed.\n");
  553. res = AST_TEST_FAIL;
  554. }
  555. ast_string_field_free_memory(reg);
  556. ast_free(reg);
  557. /* ---Test reg14, domain port without secret --- */
  558. if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
  559. goto alloc_fail;
  560. } else if (
  561. sip_parse_register_line(reg, default_expiry, reg13, 1) ||
  562. strcmp(reg->callback, "s") ||
  563. strcmp(reg->username, "name") ||
  564. strcmp(reg->regdomain, "namedomain") ||
  565. strcmp(reg->hostname, "domain") ||
  566. strcmp(reg->authuser, "") ||
  567. strcmp(reg->secret, "") ||
  568. strcmp(reg->peername, "") ||
  569. reg->transport != AST_TRANSPORT_UDP ||
  570. reg->timeout != -1 ||
  571. reg->expire != -1 ||
  572. reg->refresh != default_expiry ||
  573. reg->expiry != default_expiry ||
  574. reg->configured_expiry != default_expiry ||
  575. reg->portno != STANDARD_SIP_PORT ||
  576. reg->regdomainport != 4321 ||
  577. reg->callid_valid != FALSE ||
  578. reg->ocseq != INITIAL_CSEQ) {
  579. ast_test_status_update(test, "Test 14, domain port without secret failed.\n");
  580. res = AST_TEST_FAIL;
  581. }
  582. ast_string_field_free_memory(reg);
  583. ast_free(reg);
  584. return res;
  585. alloc_fail:
  586. ast_test_status_update(test, "Out of memory. \n");
  587. return res;
  588. }
  589. #endif
  590. int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum ast_transport *transport)
  591. {
  592. char *port;
  593. if (ast_strlen_zero(line)) {
  594. *hostname = NULL;
  595. return -1;
  596. }
  597. if ((*hostname = strstr(line, "://"))) {
  598. *hostname += 3;
  599. if (!strncasecmp(line, "tcp", 3)) {
  600. *transport = AST_TRANSPORT_TCP;
  601. } else if (!strncasecmp(line, "tls", 3)) {
  602. *transport = AST_TRANSPORT_TLS;
  603. } else if (!strncasecmp(line, "udp", 3)) {
  604. *transport = AST_TRANSPORT_UDP;
  605. } else if (lineno) {
  606. ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", line, lineno);
  607. } else {
  608. ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type in sip config. defaulting to udp.\n", line);
  609. }
  610. } else {
  611. *hostname = line;
  612. *transport = AST_TRANSPORT_UDP;
  613. }
  614. if ((line = strrchr(*hostname, '@')))
  615. line++;
  616. else
  617. line = *hostname;
  618. if (ast_sockaddr_split_hostport(line, hostname, &port, 0) == 0) {
  619. if (lineno) {
  620. ast_log(LOG_WARNING, "Cannot parse host '%s' on line %d of sip.conf.\n",
  621. line, lineno);
  622. } else {
  623. ast_log(LOG_WARNING, "Cannot parse host '%s' in sip config.\n", line);
  624. }
  625. return -1;
  626. }
  627. if (port) {
  628. if (!sscanf(port, "%5d", portnum)) {
  629. if (lineno) {
  630. ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", port, lineno);
  631. } else {
  632. ast_log(LOG_NOTICE, "'%s' is not a valid port number in sip config. using default.\n", port);
  633. }
  634. port = NULL;
  635. }
  636. }
  637. if (!port) {
  638. if (*transport & AST_TRANSPORT_TLS) {
  639. *portnum = STANDARD_TLS_PORT;
  640. } else {
  641. *portnum = STANDARD_SIP_PORT;
  642. }
  643. }
  644. return 0;
  645. }
  646. #ifdef TEST_FRAMEWORK
  647. AST_TEST_DEFINE(sip_parse_host_line_test)
  648. {
  649. int res = AST_TEST_PASS;
  650. char *host;
  651. int port;
  652. enum ast_transport transport;
  653. char host1[] = "www.blah.com";
  654. char host2[] = "tcp://www.blah.com";
  655. char host3[] = "tls://10.10.10.10";
  656. char host4[] = "tls://10.10.10.10:1234";
  657. char host5[] = "10.10.10.10:1234";
  658. switch (cmd) {
  659. case TEST_INIT:
  660. info->name = "sip_parse_host_line_test";
  661. info->category = "/channels/chan_sip/";
  662. info->summary = "tests sip.conf host line parsing";
  663. info->description =
  664. "Tests parsing of various host line configurations. "
  665. "Verifies output matches expected behavior.";
  666. return AST_TEST_NOT_RUN;
  667. case TEST_EXECUTE:
  668. break;
  669. }
  670. /* test 1, simple host */
  671. sip_parse_host(host1, 1, &host, &port, &transport);
  672. if (port != STANDARD_SIP_PORT ||
  673. ast_strlen_zero(host) || strcmp(host, "www.blah.com") ||
  674. transport != AST_TRANSPORT_UDP) {
  675. ast_test_status_update(test, "Test 1: simple host failed.\n");
  676. res = AST_TEST_FAIL;
  677. }
  678. /* test 2, add tcp transport */
  679. sip_parse_host(host2, 1, &host, &port, &transport);
  680. if (port != STANDARD_SIP_PORT ||
  681. ast_strlen_zero(host) || strcmp(host, "www.blah.com") ||
  682. transport != AST_TRANSPORT_TCP) {
  683. ast_test_status_update(test, "Test 2: tcp host failed.\n");
  684. res = AST_TEST_FAIL;
  685. }
  686. /* test 3, add tls transport */
  687. sip_parse_host(host3, 1, &host, &port, &transport);
  688. if (port != STANDARD_TLS_PORT ||
  689. ast_strlen_zero(host) || strcmp(host, "10.10.10.10") ||
  690. transport != AST_TRANSPORT_TLS) {
  691. ast_test_status_update(test, "Test 3: tls host failed. \n");
  692. res = AST_TEST_FAIL;
  693. }
  694. /* test 4, add custom port with tls */
  695. sip_parse_host(host4, 1, &host, &port, &transport);
  696. if (port != 1234 || ast_strlen_zero(host) ||
  697. strcmp(host, "10.10.10.10") ||
  698. transport != AST_TRANSPORT_TLS) {
  699. ast_test_status_update(test, "Test 4: tls host with custom port failed.\n");
  700. res = AST_TEST_FAIL;
  701. }
  702. /* test 5, simple host with custom port */
  703. sip_parse_host(host5, 1, &host, &port, &transport);
  704. if (port != 1234 || ast_strlen_zero(host) ||
  705. strcmp(host, "10.10.10.10") ||
  706. transport != AST_TRANSPORT_UDP) {
  707. ast_test_status_update(test, "Test 5: simple host with custom port failed.\n");
  708. res = AST_TEST_FAIL;
  709. }
  710. /* test 6, expected failure with NULL input */
  711. if (!sip_parse_host(NULL, 1, &host, &port, &transport)) {
  712. ast_test_status_update(test, "Test 6: expected error on NULL input did not occur.\n");
  713. res = AST_TEST_FAIL;
  714. }
  715. return res;
  716. }
  717. #endif
  718. /*! \brief Parse the comma-separated nat= option values */
  719. void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags)
  720. {
  721. char *parse, *this;
  722. if (!(parse = ast_strdupa(value))) {
  723. return;
  724. }
  725. /* Since we need to completely override the general settings if we are being called
  726. * later for a peer, always set the flags for all options on the mask */
  727. ast_set_flag(&mask[0], SIP_NAT_FORCE_RPORT);
  728. ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
  729. ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_RPORT);
  730. ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
  731. while ((this = strsep(&parse, ","))) {
  732. if (ast_false(this)) {
  733. ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
  734. ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
  735. ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
  736. ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
  737. break; /* It doesn't make sense to have no + something else */
  738. } else if (!strcasecmp(this, "yes")) {
  739. ast_log(LOG_WARNING, "nat=yes is deprecated, use nat=force_rport,comedia instead\n");
  740. ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
  741. ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
  742. ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
  743. ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
  744. break; /* It doesn't make sense to have yes + something else */
  745. } else if (!strcasecmp(this, "force_rport") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
  746. ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
  747. } else if (!strcasecmp(this, "comedia") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
  748. ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
  749. } else if (!strcasecmp(this, "auto_force_rport")) {
  750. ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
  751. /* In case someone did something dumb like nat=force_rport,auto_force_rport */
  752. ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
  753. } else if (!strcasecmp(this, "auto_comedia")) {
  754. ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
  755. /* In case someone did something dumb like nat=comedia,auto_comedia*/
  756. ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
  757. }
  758. }
  759. }
  760. #ifdef TEST_FRAMEWORK
  761. #define TEST_FORCE_RPORT 1 << 0
  762. #define TEST_COMEDIA 1 << 1
  763. #define TEST_AUTO_FORCE_RPORT 1 << 2
  764. #define TEST_AUTO_COMEDIA 1 << 3
  765. static int match_nat_options(int val, struct ast_flags *flags)
  766. {
  767. if ((!ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT)) != !(val & TEST_FORCE_RPORT)) {
  768. return 0;
  769. }
  770. if (!ast_test_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP) != !(val & TEST_COMEDIA)) {
  771. return 0;
  772. }
  773. if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT) != !(val & TEST_AUTO_FORCE_RPORT)) {
  774. return 0;
  775. }
  776. if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) != !(val & TEST_AUTO_COMEDIA)) {
  777. return 0;
  778. }
  779. return 1;
  780. }
  781. AST_TEST_DEFINE(sip_parse_nat_test)
  782. {
  783. int i, res = AST_TEST_PASS;
  784. struct ast_flags mask[3] = {{0}}, flags[3] = {{0}};
  785. struct {
  786. const char *str;
  787. int i;
  788. } options[] = {
  789. { "yes", TEST_FORCE_RPORT | TEST_COMEDIA },
  790. { "no", 0 },
  791. { "force_rport", TEST_FORCE_RPORT },
  792. { "comedia", TEST_COMEDIA },
  793. { "auto_force_rport", TEST_AUTO_FORCE_RPORT },
  794. { "auto_comedia", TEST_AUTO_COMEDIA },
  795. { "force_rport,auto_force_rport", TEST_AUTO_FORCE_RPORT },
  796. { "auto_force_rport,force_rport", TEST_AUTO_FORCE_RPORT },
  797. { "comedia,auto_comedia", TEST_AUTO_COMEDIA },
  798. { "auto_comedia,comedia", TEST_AUTO_COMEDIA },
  799. { "force_rport,comedia", TEST_FORCE_RPORT | TEST_COMEDIA },
  800. { "force_rport,auto_comedia", TEST_FORCE_RPORT | TEST_AUTO_COMEDIA },
  801. { "force_rport,yes,no", TEST_FORCE_RPORT | TEST_COMEDIA },
  802. { "auto_comedia,no,yes", 0 },
  803. };
  804. switch (cmd) {
  805. case TEST_INIT:
  806. info->name = "sip_parse_nat_test";
  807. info->category = "/channels/chan_sip/";
  808. info->summary = "tests sip.conf nat line parsing";
  809. info->description =
  810. "Tests parsing of various nat line configurations. "
  811. "Verifies output matches expected behavior.";
  812. return AST_TEST_NOT_RUN;
  813. case TEST_EXECUTE:
  814. break;
  815. }
  816. for (i = 0; i < ARRAY_LEN(options); i++) {
  817. sip_parse_nat_option(options[i].str, mask, flags);
  818. if (!match_nat_options(options[i].i, flags)) {
  819. ast_test_status_update(test, "Failed nat=%s\n", options[i].str);
  820. res = AST_TEST_FAIL;
  821. }
  822. memset(flags, 0, sizeof(flags));
  823. memset(mask, 0, sizeof(mask));
  824. }
  825. return res;
  826. }
  827. #endif
  828. /*! \brief SIP test registration */
  829. void sip_config_parser_register_tests(void)
  830. {
  831. AST_TEST_REGISTER(sip_parse_register_line_test);
  832. AST_TEST_REGISTER(sip_parse_host_line_test);
  833. AST_TEST_REGISTER(sip_parse_nat_test);
  834. }
  835. /*! \brief SIP test registration */
  836. void sip_config_parser_unregister_tests(void)
  837. {
  838. AST_TEST_UNREGISTER(sip_parse_register_line_test);
  839. AST_TEST_UNREGISTER(sip_parse_host_line_test);
  840. AST_TEST_UNREGISTER(sip_parse_nat_test);
  841. }