Asterisk - The Open Source Telephony Project  18.5.0
config_parser.c
Go to the documentation of this file.
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] */
46  AST_APP_ARG(peer);
47  AST_APP_ARG(userpart);
48  );
50  AST_APP_ARG(transport);
51  AST_APP_ARG(blank);
52  AST_APP_ARG(userpart);
53  );
55  AST_APP_ARG(userpart);
56  AST_APP_ARG(secret);
57  AST_APP_ARG(authuser);
58  );
62  );
64  AST_APP_ARG(authuser);
65  AST_APP_ARG(domainport);
66  );
68  AST_APP_ARG(hostpart);
69  AST_APP_ARG(expiry);
70  );
72  AST_APP_ARG(hostpart);
74  );
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  }
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  }
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  }
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  }
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  }
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  }
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  }
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  }
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)) {
544  "Test 9, missing domain, expected to fail but did not.\n");
545  res = AST_TEST_FAIL;
546  }
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)) {
555  "Test 10, missing user expected to fail but did not\n");
556  res = AST_TEST_FAIL;
557  }
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)) {
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 
574  "Test 12, NULL register line expected to fail but did not.\n");
575  res = AST_TEST_FAIL;
576  }
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  }
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  }
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;
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 */
808 
809  while ((this = strsep(&parse, ","))) {
810  if (ast_false(this)) {
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);
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)) {
827  } else if (!strcasecmp(this, "auto_force_rport")) {
829  /* In case someone did something dumb like nat=force_rport,auto_force_rport */
831  } else if (!strcasecmp(this, "auto_comedia")) {
833  /* In case someone did something dumb like nat=comedia,auto_comedia*/
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++) {
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 */
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 */
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 }
chan_sip header file
Asterisk main include file. File version handling, generic pbx functions.
#define FALSE
Definition: app_minivm.c:521
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
const ast_string_field hostname
Definition: sip.h:1414
int expire
Definition: sip.h:1418
Definition: ast_expr2.c:325
const ast_string_field username
Definition: sip.h:1414
#define ast_test_flag(p, flag)
Definition: utils.h:63
int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const char *value, int lineno)
Parse register=> line in sip.conf.
Definition: config_parser.c:37
int portno
Definition: sip.h:1416
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
ast_transport
Definition: netsock2.h:59
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define LOG_WARNING
Definition: logger.h:274
unsigned int flags
Definition: utils.h:200
int configured_expiry
Definition: sip.h:1419
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
Registrations with other SIP proxies.
Definition: sip.h:1396
#define SIP_NAT_FORCE_RPORT
Definition: sip.h:283
#define ast_calloc_with_stringfields(n, type, size)
Allocate a structure with embedded stringfields in a single allocation.
Definition: stringfields.h:426
void sip_config_parser_register_tests(void)
SIP test registration.
const char * str
Definition: app_jack.c:147
#define NULL
Definition: resample.c:96
Domain data structure.
Definition: sip.h:888
int value
Definition: syslog.c:37
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define TEST_COMEDIA
#define TEST_AUTO_FORCE_RPORT
void sip_config_parser_unregister_tests(void)
SIP test registration.
int regdomainport
Definition: sip.h:1417
const ast_string_field authuser
Definition: sip.h:1414
#define SIP_PAGE2_SYMMETRICRTP
Definition: sip.h:327
char * ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
Strip leading/trailing whitespace and quotes from a string.
Definition: main/utils.c:1639
#define ast_log
Definition: astobj2.c:42
#define STANDARD_TLS_PORT
Standard SIP TLS port from RFC 3261. DO NOT CHANGE THIS.
Definition: sip.h:178
static char host[256]
Definition: muted.c:77
int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
Splits a string into its host and port components.
Definition: netsock2.c:164
#define STANDARD_SIP_PORT
Standard SIP unsecure port for UDP and TCP from RFC 3261. DO NOT CHANGE THIS.
Definition: sip.h:176
static int match_nat_options(int val, struct ast_flags *flags)
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
structure to hold extensions
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
uint32_t ocseq
Definition: sip.h:1428
int callid_valid
Definition: sip.h:1427
#define AST_NONSTANDARD_RAW_ARGS(args, parse, sep)
static int default_expiry
Definition: chan_sip.c:669
#define TEST_AUTO_COMEDIA
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
#define LOG_NOTICE
Definition: logger.h:263
#define SIP_PAGE3_NAT_AUTO_RPORT
Definition: sip.h:386
AST_TEST_DEFINE(sip_parse_register_line_test)
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define ast_free(a)
Definition: astmm.h:182
void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags)
Parse the comma-separated nat= option values.
#define INITIAL_CSEQ
Definition: sip.h:117
sip.conf parser header file
structure to hold users read from users.conf
Structure used to handle boolean flags.
Definition: utils.h:199
int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum ast_transport *transport)
parses a config line for a host with a transport
const ast_string_field secret
Definition: sip.h:1414
#define ast_clear_flag(p, flag)
Definition: utils.h:77
int expiry
Definition: sip.h:1420
#define SIP_PAGE3_NAT_AUTO_COMEDIA
Definition: sip.h:387
char * strsep(char **str, const char *delims)
const ast_string_field peername
Definition: sip.h:1414
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: main/utils.c:1968
unsigned int port_str2int(const char *pt, unsigned int standard)
converts ascii port to int representation. If no pt buffer is provided or the pt has errors when bein...
Definition: chan_sip.c:3538
int timeout
Definition: sip.h:1422
const ast_string_field regdomain
Definition: sip.h:1414
static struct ast_str * hostname
Definition: cdr_mysql.c:77
#define TEST_FORCE_RPORT
static struct test_options options
const ast_string_field callback
Definition: sip.h:1414
enum ast_transport transport
Definition: sip.h:1415
sip utils header file
int refresh
Definition: sip.h:1423
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
#define AST_APP_ARG(name)
Define an application argument.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514