30
30
#include <pjsip.h>
31
31
32
32
#include "asterisk/res_pjsip.h"
33
+ #include "asterisk/vector.h"
33
34
#include "asterisk/module.h"
34
35
#include "asterisk/logger.h"
35
36
#include "asterisk/cli.h"
@@ -99,6 +100,12 @@ struct pcap_udp_header {
99
100
uint16_t checksum ; /*! \brief Packet checksum, left uncalculated for our purposes */
100
101
};
101
102
103
+ struct method_logging_info {
104
+ pj_str_t pj_name ; /*! \brief A PJSIP string for the method */
105
+ pjsip_method method ; /*! \brief The PJSIP method structure used for comparisons */
106
+ char name []; /*! \brief The method name */
107
+ };
108
+
102
109
/*! \brief PJSIP Logging Session */
103
110
struct pjsip_logger_session {
104
111
/*! \brief Explicit addresses or ranges being logged */
@@ -115,6 +122,8 @@ struct pjsip_logger_session {
115
122
unsigned int log_to_verbose :1 ;
116
123
/*! \brief Whether to log to pcap or not */
117
124
unsigned int log_to_pcap :1 ;
125
+ /*! \brief Vector of SIP methods to log */
126
+ AST_VECTOR (, struct method_logging_info * ) log_methods ;
118
127
};
119
128
120
129
/*! \brief The default logger session */
@@ -130,6 +139,9 @@ static void pjsip_logger_session_destroy(void *obj)
130
139
}
131
140
132
141
ast_free_ha (session -> matches );
142
+
143
+ AST_VECTOR_RESET (& session -> log_methods , ast_free );
144
+ AST_VECTOR_FREE (& session -> log_methods );
133
145
}
134
146
135
147
/*! \brief Allocator for logger session */
@@ -145,11 +157,35 @@ static struct pjsip_logger_session *pjsip_logger_session_alloc(void)
145
157
146
158
session -> log_to_verbose = 1 ;
147
159
160
+ AST_VECTOR_INIT (& session -> log_methods , 0 );
161
+
148
162
return session ;
149
163
}
150
164
151
- /*! \brief See if we pass debug IP filter */
152
- static inline int pjsip_log_test_addr (const struct pjsip_logger_session * session , const char * address , int port )
165
+ /*! \note Must be called with the pjsip_logger_session lock held */
166
+ static int apply_method_filter (const struct pjsip_logger_session * session , const pjsip_method * method )
167
+ {
168
+ size_t size = AST_VECTOR_SIZE (& session -> log_methods );
169
+ size_t i ;
170
+
171
+ if (size == 0 ) {
172
+ /* Nothing in the vector means everything matches */
173
+ return 0 ;
174
+ }
175
+
176
+ for (i = 0 ; i < size ; ++ i ) {
177
+ struct method_logging_info * candidate = AST_VECTOR_GET (& session -> log_methods , i );
178
+ if (pjsip_method_cmp (& candidate -> method , method ) == 0 ) {
179
+ return 0 ;
180
+ }
181
+ }
182
+
183
+ /* Nothing matched */
184
+ return 1 ;
185
+ }
186
+
187
+ /*! \brief See if we pass debug filter */
188
+ static inline int pjsip_log_test_filter (const struct pjsip_logger_session * session , const char * address , int port , const pjsip_method * method )
153
189
{
154
190
struct ast_sockaddr test_addr ;
155
191
@@ -161,9 +197,15 @@ static inline int pjsip_log_test_addr(const struct pjsip_logger_session *session
161
197
return 1 ;
162
198
}
163
199
200
+ if (apply_method_filter (session , method )) {
201
+ /* The method filter didn't match anything, so reject. */
202
+ return 0 ;
203
+ }
204
+
164
205
/* A null address was passed in or no explicit matches. Just reject it. */
165
206
if (ast_strlen_zero (address ) || !session -> matches ) {
166
- return 0 ;
207
+ /* If we matched on method and host is empty, accept, otherwise reject. */
208
+ return AST_VECTOR_SIZE (& session -> log_methods ) > 0 ;
167
209
}
168
210
169
211
ast_sockaddr_parse (& test_addr , address , PARSE_PORT_IGNORE );
@@ -270,7 +312,7 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
270
312
char buffer [AST_SOCKADDR_BUFLEN ];
271
313
272
314
ao2_rdlock (default_logger );
273
- if (!pjsip_log_test_addr (default_logger , tdata -> tp_info .dst_name , tdata -> tp_info .dst_port )) {
315
+ if (!pjsip_log_test_filter (default_logger , tdata -> tp_info .dst_name , tdata -> tp_info .dst_port , & tdata -> msg -> line . req . method )) {
274
316
ao2_unlock (default_logger );
275
317
return PJ_SUCCESS ;
276
318
}
@@ -302,7 +344,7 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
302
344
}
303
345
304
346
ao2_rdlock (default_logger );
305
- if (!pjsip_log_test_addr (default_logger , rdata -> pkt_info .src_name , rdata -> pkt_info .src_port )) {
347
+ if (!pjsip_log_test_filter (default_logger , rdata -> pkt_info .src_name , rdata -> pkt_info .src_port , & rdata -> msg_info . msg -> line . req . method )) {
306
348
ao2_unlock (default_logger );
307
349
return PJ_FALSE ;
308
350
}
@@ -393,6 +435,93 @@ static char *pjsip_enable_logger_host(int fd, const char *arg, unsigned int add_
393
435
return CLI_SUCCESS ;
394
436
}
395
437
438
+ static struct method_logging_info * method_logging_info_alloc (const char * method )
439
+ {
440
+ size_t method_bytes = strlen (method );
441
+ struct method_logging_info * info ;
442
+
443
+ info = ast_calloc (1 , sizeof (struct method_logging_info ) + method_bytes + 1 );
444
+ if (!info ) {
445
+ return NULL ;
446
+ }
447
+
448
+ memcpy (info -> name , method , method_bytes + 1 );
449
+ pj_strset (& info -> pj_name , info -> name , method_bytes );
450
+ pjsip_method_init_np (& info -> method , & info -> pj_name );
451
+
452
+ return info ;
453
+ }
454
+
455
+ static int method_logging_info_cmp (const struct method_logging_info * element ,
456
+ const struct method_logging_info * candidate )
457
+ {
458
+ return pjsip_method_cmp (& element -> method , & candidate -> method ) == 0
459
+ ? CMP_MATCH | CMP_STOP
460
+ : 0 ;
461
+ }
462
+
463
+ static int method_logging_info_sort_cmp (const void * a , const void * b )
464
+ {
465
+ const struct method_logging_info * const * m_a = a ;
466
+ const struct method_logging_info * const * m_b = b ;
467
+ return strcasecmp ((* m_a )-> name , (* m_b )-> name );
468
+ }
469
+
470
+ /*! \brief Add the current or an additional method to match for filtering */
471
+ static char * pjsip_enable_logger_method (int fd , const char * arg , int add_method )
472
+ {
473
+ struct ast_str * str ;
474
+ struct method_logging_info * method ;
475
+
476
+ method = method_logging_info_alloc (arg );
477
+ if (!method ) {
478
+ return CLI_FAILURE ;
479
+ }
480
+
481
+ ao2_wrlock (default_logger );
482
+ default_logger -> enabled = 1 ;
483
+
484
+ if (!add_method ) {
485
+ /* Remove what already exists */
486
+ AST_VECTOR_RESET (& default_logger -> log_methods , ast_free );
487
+ }
488
+
489
+ /* Already in the list? */
490
+ if (AST_VECTOR_CALLBACK (& default_logger -> log_methods , method_logging_info_cmp , NULL , method ) != NULL ) {
491
+ ast_cli (fd , "Method '%s' is already enabled\n" , method -> name );
492
+ ao2_unlock (default_logger );
493
+ ast_free (method );
494
+ return CLI_SUCCESS ;
495
+ }
496
+
497
+ if (AST_VECTOR_APPEND (& default_logger -> log_methods , method )) {
498
+ ast_log (LOG_ERROR , "Cannot register logger method '%s'. Unable to append.\n" , method -> name );
499
+ ao2_unlock (default_logger );
500
+ ast_free (method );
501
+ return CLI_SUCCESS ;
502
+ }
503
+
504
+ AST_VECTOR_SORT (& default_logger -> log_methods , method_logging_info_sort_cmp );
505
+
506
+ str = ast_str_create (256 );
507
+ if (str ) {
508
+ size_t i ;
509
+ for (i = 0 ; i < AST_VECTOR_SIZE (& default_logger -> log_methods ); i ++ ) {
510
+ method = AST_VECTOR_GET (& default_logger -> log_methods , i );
511
+ ast_str_append (& str , 0 , "%s%.*s" ,
512
+ ast_str_strlen (str ) ? ", " : "" ,
513
+ (int ) method -> pj_name .slen , method -> pj_name .ptr );
514
+ }
515
+
516
+ ast_cli (fd , "PJSIP Logging Enabled for SIP Methods: %s\n" , ast_str_buffer (str ));
517
+ ast_free (str );
518
+ }
519
+
520
+ ao2_unlock (default_logger );
521
+
522
+ return CLI_SUCCESS ;
523
+ }
524
+
396
525
static char * pjsip_disable_logger (int fd )
397
526
{
398
527
ao2_wrlock (default_logger );
@@ -404,6 +533,8 @@ static char *pjsip_disable_logger(int fd)
404
533
default_logger -> log_to_verbose = 1 ;
405
534
default_logger -> log_to_pcap = 0 ;
406
535
536
+ AST_VECTOR_RESET (& default_logger -> log_methods , ast_free );
537
+
407
538
/* Stop logging to the PCAP file if active */
408
539
if (default_logger -> pcap_file ) {
409
540
fclose (default_logger -> pcap_file );
@@ -469,18 +600,32 @@ static char *pjsip_set_logger_pcap(int fd, const char *arg)
469
600
470
601
static char * pjsip_set_logger (struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
471
602
{
603
+ static const char * const method_choices [] = {
604
+ "INVITE" , "CANCEL" , "ACK" ,
605
+ "BYE" , "REGISTER" , "OPTION" ,
606
+ "SUBSCRIBE" , "NOTIFY" , "PUBLISH" ,
607
+ "INFO" , "MESSAGE" ,
608
+ NULL
609
+ };
610
+
472
611
const char * what ;
473
612
474
613
if (cmd == CLI_INIT ) {
475
- e -> command = "pjsip set logger {on|off|host|add|verbose|pcap}" ;
614
+ e -> command = "pjsip set logger {on|off|host|add|method|methodadd| verbose|pcap}" ;
476
615
e -> usage =
477
- "Usage: pjsip set logger {on|off|host <name/subnet>|add <name/subnet>|verbose <on/off>|pcap <filename>}\n"
616
+ "Usage: pjsip set logger {on|off|host <name/subnet>|add <name/subnet>|method <method>|methodadd <method>| verbose <on/off>|pcap <filename>}\n"
478
617
" Enables or disabling logging of SIP packets\n"
479
618
" read on ports bound to PJSIP transports either\n"
480
619
" globally or enables logging for an individual\n"
481
- " host.\n" ;
620
+ " host or particular SIP method(s).\n"
621
+ " Messages can be filtered by SIP request methods\n"
622
+ " INVITE, CANCEL, ACK, BYE, REGISTER, OPTION\n"
623
+ " SUBSCRIBE, NOTIFY, PUBLISH, INFO, and MESSAGE\n" ;
482
624
return NULL ;
483
625
} else if (cmd == CLI_GENERATE ) {
626
+ if (a -> argc && !strncasecmp (a -> argv [e -> args - 1 ], "method" , 6 )) {
627
+ return ast_cli_complete (a -> word , method_choices , a -> n );
628
+ }
484
629
return NULL ;
485
630
}
486
631
@@ -497,6 +642,10 @@ static char *pjsip_set_logger(struct ast_cli_entry *e, int cmd, struct ast_cli_a
497
642
return pjsip_enable_logger_host (a -> fd , a -> argv [e -> args ], 0 );
498
643
} else if (!strcasecmp (what , "add" )) {
499
644
return pjsip_enable_logger_host (a -> fd , a -> argv [e -> args ], 1 );
645
+ } else if (!strcasecmp (what , "method" )) {
646
+ return pjsip_enable_logger_method (a -> fd , a -> argv [e -> args ], 0 );
647
+ } else if (!strcasecmp (what , "methodadd" )) {
648
+ return pjsip_enable_logger_method (a -> fd , a -> argv [e -> args ], 1 );
500
649
} else if (!strcasecmp (what , "verbose" )) {
501
650
return pjsip_set_logger_verbose (a -> fd , a -> argv [e -> args ]);
502
651
} else if (!strcasecmp (what , "pcap" )) {
0 commit comments