res_pjsip: Updates and adds more PJSIP CLI commands.
[asterisk/asterisk.git] / res / res_pjsip_logger.c
index a013bb5..a69ca42 100644 (file)
 /*** MODULEINFO
        <depend>pjproject</depend>
        <depend>res_pjsip</depend>
+       <defaultenabled>yes</defaultenabled>
        <support_level>core</support_level>
  ***/
 
 #include "asterisk.h"
 
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
 #include <pjsip.h>
 
 #include "asterisk/res_pjsip.h"
 #include "asterisk/module.h"
 #include "asterisk/logger.h"
+#include "asterisk/cli.h"
+#include "asterisk/netsock2.h"
+
+enum pjsip_logging_mode {
+       LOGGING_MODE_DISABLED,    /* No logging is enabled */
+       LOGGING_MODE_ENABLED,     /* Logging is enabled */
+};
+
+static enum pjsip_logging_mode logging_mode;
+static struct ast_sockaddr log_addr;
+
+/*! \brief  Return the first entry from ast_sockaddr_resolve filtered by address family
+ *
+ * \warning Using this function probably means you have a faulty design.
+ * \note This function was taken from the function of the same name in chan_sip.c
+ */
+static int ast_sockaddr_resolve_first_af(struct ast_sockaddr *addr,
+                                     const char* name, int flag, int family)
+{
+       struct ast_sockaddr *addrs;
+       int addrs_cnt;
+
+       addrs_cnt = ast_sockaddr_resolve(&addrs, name, flag, family);
+       if (addrs_cnt <= 0) {
+               return 1;
+       }
+       if (addrs_cnt > 1) {
+               ast_debug(1, "Multiple addresses, using the first one only\n");
+       }
+
+       ast_sockaddr_copy(addr, &addrs[0]);
+
+       ast_free(addrs);
+       return 0;
+}
+
+/*! \brief See if we pass debug IP filter */
+static inline int pjsip_log_test_addr(const char *address, int port)
+{
+       struct ast_sockaddr test_addr;
+       if (logging_mode == LOGGING_MODE_DISABLED) {
+               return 0;
+       }
+
+       /* A null logging address means we'll debug any address */
+       if (ast_sockaddr_isnull(&log_addr)) {
+               return 1;
+       }
+
+       /* A null address was passed in. Just reject it. */
+       if (ast_strlen_zero(address)) {
+               return 0;
+       }
+
+       ast_sockaddr_parse(&test_addr, address, PARSE_PORT_IGNORE);
+       ast_sockaddr_set_port(&test_addr, port);
+
+       /* If no port was specified for a debug address, just compare the
+        * addresses, otherwise compare the address and port
+        */
+       if (ast_sockaddr_port(&log_addr)) {
+               return !ast_sockaddr_cmp(&log_addr, &test_addr);
+       } else {
+               return !ast_sockaddr_cmp_addr(&log_addr, &test_addr);
+       }
+}
 
 static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
 {
+       if (!pjsip_log_test_addr(tdata->tp_info.dst_name, tdata->tp_info.dst_port)) {
+               return PJ_SUCCESS;
+       }
+
        ast_verbose("<--- Transmitting SIP %s (%d bytes) to %s:%s:%d --->\n%.*s\n",
                    tdata->msg->type == PJSIP_REQUEST_MSG ? "request" : "response",
                    (int) (tdata->buf.cur - tdata->buf.start),
@@ -44,6 +117,10 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
 
 static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
 {
+       if (!pjsip_log_test_addr(rdata->pkt_info.src_name, rdata->pkt_info.src_port)) {
+               return PJ_FALSE;
+       }
+
        ast_verbose("<--- Received SIP %s (%d bytes) from %s:%s:%d --->\n%s\n",
                    rdata->msg_info.msg->type == PJSIP_REQUEST_MSG ? "request" : "response",
                    rdata->msg_info.len,
@@ -63,15 +140,116 @@ static pjsip_module logging_module = {
        .on_tx_response = logging_on_tx_msg,
 };
 
+static char *pjsip_enable_logger_host(int fd, const char *arg)
+{
+       if (ast_sockaddr_resolve_first_af(&log_addr, arg, 0, AST_AF_UNSPEC)) {
+               return CLI_SHOWUSAGE;
+       }
+
+       ast_cli(fd, "PJSIP Logging Enabled for host: %s\n", ast_sockaddr_stringify_addr(&log_addr));
+       logging_mode = LOGGING_MODE_ENABLED;
+
+       return CLI_SUCCESS;
+}
+
+static char *pjsip_set_logger(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       const char *what;
+
+       if (cmd == CLI_INIT) {
+               e->command = "pjsip set logger {on|off|host}";
+               e->usage =
+                       "Usage: pjsip set logger {on|off|host <name>}\n"
+                       "       Enables or disabling logging of SIP packets\n"
+                       "       read on ports bound to PJSIP transports either\n"
+                       "       globally or enables logging for an individual\n"
+                       "       host.\n";
+               return NULL;
+       } else if (cmd == CLI_GENERATE) {
+               return NULL;
+       }
+
+       what = a->argv[e->args - 1];     /* Guaranteed to exist */
+
+       if (a->argc == e->args) {        /* on/off */
+               if (!strcasecmp(what, "on")) {
+                       logging_mode = LOGGING_MODE_ENABLED;
+                       ast_cli(a->fd, "PJSIP Logging enabled\n");
+                       ast_sockaddr_setnull(&log_addr);
+                       return CLI_SUCCESS;
+               } else if (!strcasecmp(what, "off")) {
+                       logging_mode = LOGGING_MODE_DISABLED;
+                       ast_cli(a->fd, "PJSIP Logging disabled\n");
+                       return CLI_SUCCESS;
+               }
+       } else if (a->argc == e->args + 1) {
+               if (!strcasecmp(what, "host")) {
+                       return pjsip_enable_logger_host(a->fd, a->argv[e->args]);
+               }
+       }
+
+       return CLI_SHOWUSAGE;
+}
+
+static struct ast_cli_entry cli_pjsip[] = {
+       AST_CLI_DEFINE(pjsip_set_logger, "Enable/Disable PJSIP Logger Output")
+};
+
+static void check_debug(void)
+{
+       RAII_VAR(char *, debug, ast_sip_get_debug(), ast_free);
+
+       if (ast_false(debug)) {
+               logging_mode = LOGGING_MODE_DISABLED;
+               return;
+       }
+
+       logging_mode = LOGGING_MODE_ENABLED;
+
+       if (ast_true(debug)) {
+               ast_sockaddr_setnull(&log_addr);
+               return;
+       }
+
+       /* assume host */
+       if (ast_sockaddr_resolve_first_af(&log_addr, debug, 0, AST_AF_UNSPEC)) {
+               ast_log(LOG_WARNING, "Could not resolve host %s for debug "
+                       "logging\n", debug);
+       }
+}
+
+static void global_reloaded(const char *object_type)
+{
+       check_debug();
+}
+
+static const struct ast_sorcery_observer global_observer = {
+       .loaded = global_reloaded
+};
+
 static int load_module(void)
 {
+       if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer)) {
+               ast_log(LOG_WARNING, "Unable to add global observer\n");
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       check_debug();
+
        ast_sip_register_service(&logging_module);
+       ast_cli_register_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip));
+
        return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int unload_module(void)
 {
+       ast_cli_unregister_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip));
        ast_sip_unregister_service(&logging_module);
+
+       ast_sorcery_observer_remove(
+               ast_sip_get_sorcery(), "global", &global_observer);
+
        return 0;
 }