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