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