]> mj.ucw.cz Git - siplog.git/commitdiff
Initial commit
authorMartin Mares <mj@ucw.cz>
Sat, 5 Jan 2008 23:17:50 +0000 (00:17 +0100)
committerMartin Mares <mj@ucw.cz>
Sat, 5 Jan 2008 23:17:50 +0000 (00:17 +0100)
Makefile [new file with mode: 0644]
siplog.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..9df7568
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+LD=gcc
+CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 -fPIC
+CPPFLAGS+=-I/tmp/ulogd-1.23/include -I/tmp/ulogd-1.23/libipulog/include
+
+all: siplog.so
+
+siplog.so: siplog.o
+       $(LD) -shared $^ -o $@
+
+clean:
+       rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core -or -name .depend -or -name .#*`
+       rm -f siplog.so
diff --git a/siplog.c b/siplog.c
new file mode 100644 (file)
index 0000000..81d36a8
--- /dev/null
+++ b/siplog.c
@@ -0,0 +1,317 @@
+/*
+ *     A Simple SIP Logging Plugin for ulogd
+ *
+ *     (c) 2008 Martin Mares <mj@ucw.cz>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <alloca.h>
+#include <ulogd/ulogd.h>
+#include <ulogd/conffile.h>
+
+typedef unsigned char byte;
+
+/*** Configuration ***/
+
+static config_entry_t sip_cf_log = {
+  .next = NULL,
+  .key = "file",
+  .type = CONFIG_TYPE_STRING,
+  .options = CONFIG_OPT_NONE,
+  .u.string = "/var/log/ulog/sip.log"
+};
+
+/*** Unaligned access ***/
+
+static unsigned int get_u16(byte *p)
+{
+  return (p[0] << 8) | p[1];
+}
+
+static unsigned int get_u32(byte *p)
+{
+  return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+/*** Keys ***/
+
+static struct {
+  char *name;
+  unsigned int id;
+} keys[] = {
+  { "raw.pkt", 0 },
+  { "raw.pktlen", 0 },
+  { "oob.time.sec", 0 },
+  { "oob.time.usec", 0 },
+  { "oob.prefix", 0 },
+};
+
+enum {
+  K_RAW_PKT = 0,
+  K_RAW_PKTLEN,
+  K_OOB_TIME_SEC,
+  K_OOB_TIME_USEC,
+  K_OOB_PREFIX,
+};
+
+static int init_keys(void)
+{
+  for (unsigned int i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
+    if (!(keys[i].id = keyh_getid(keys[i].name)))
+      {
+        ulogd_log(ULOGD_ERROR, "SIP: Unable to resolve key %s\n", keys[i].name);
+       return 1;
+      }
+  return 0;
+}
+
+#define KEY_H(i) ulogd_keyh[keys[i].id].interp->result[ulogd_keyh[keys[i].id].offset]
+#define KEY_VAL(i) KEY_H(i).value
+#define KEY_FLAGS(i) KEY_H(i).flags
+#define KEY_TYPE(i) KEY_H(i).type
+#define KEY_OK(i,t) ((KEY_FLAGS(i) & ULOGD_RETF_VALID) && KEY_TYPE(i) == ULOGD_RET_##t)
+
+/*** Logging ***/
+
+static FILE *siplog;
+
+struct hdr {
+  struct hdr *next;
+  char *name, *value;
+};
+
+static void sip_parse_obj(char *rr, struct hdr *headers)
+{
+}
+
+static void sip_parse_header(byte *pkt, unsigned int len)
+{
+  char *p = (char*) pkt;
+  char *end = p + len;
+  struct hdr *headers = NULL;
+
+#if 0
+  for (unsigned int i=0; i<len; i++)
+    fputc(p[i], siplog);
+  fprintf(siplog, "----\n");
+#endif
+
+  // Parse the request/reply line first
+  char *rr = p;
+  while (p < end && *p != '\r' && *p != '\n')
+    p++;
+  if (p >= end)
+    {
+      fprintf(siplog, "\tTruncated 1st line\n");
+      return;
+    }
+  if (*p == '\r')
+    *p++ = 0;
+  if (p < end && *p == '\n')
+    *p++ = 0;
+  fprintf(siplog, "\t>>> %s\n", rr);
+
+  // Slurp the other header lines
+  for (;;)
+    {
+      char *start = p;
+      char *w = start;
+      for (;;)
+        {
+         if (p >= end)
+           {
+             fprintf(siplog, "\tTruncated SIP headers\n");
+             return;
+           }
+         if (*p == '\r')
+           p++;
+         else if (*p == '\n')
+           {
+             p++;
+             if (p < end && (*p == ' ' || *p == '\t'))
+               ;
+             else
+               {
+                 *w++ = 0;
+                 break;
+               }
+           }
+         else
+           *w++ = *p++;
+       }
+      if (!start[0])
+        break;
+      // fprintf(siplog, "\tH: %s\n", start);
+      // Got a line, parse it
+      char *sep = strchr(start, ':');
+      if (!*sep)
+        {
+         fprintf(siplog, "\tMalformed header line\n");
+         continue;
+       }
+      *sep = 0;
+      for (char *p=sep; p>start && (p[-1] == ' ' || p[-1] == '\t');)
+        *--p = 0;
+      while (++sep < end && (*sep == ' ' || *sep == '\t'))
+        *sep = 0;
+      fprintf(siplog, "\t%s: %s\n", start, sep);
+      struct hdr *h = alloca(sizeof(*h));
+      h->next = headers;
+      headers = h;
+      h->name = start;
+      h->value = sep;
+    }
+
+  sip_parse_obj(rr, headers);
+}
+
+static void sip_parse_packet(byte *pkt, unsigned int len)
+{
+  /* Parse IP header */
+  if (len < 20)
+    {
+      fprintf(siplog, "\tVery truncated IP header\n");
+      return;
+    }
+  unsigned int ipver = pkt[0] >> 4;
+  if (ipver != 4)
+    {
+      fprintf(siplog, "\tWe don't speak IPv%d\n", ipver);
+      return;
+    }
+  unsigned int hlen = (pkt[0] & 0x0f)*4;
+  if (len < hlen)
+    {
+      fprintf(siplog, "\tTruncated IP header (want %d)\n", hlen);
+      return;
+    }
+  unsigned int ilen = get_u16(pkt+2);
+  if (ilen > len)
+    {
+      fprintf(siplog, "\tTruncated (have %d, want %d)\n", len, ilen);
+      return;
+    }
+  len = ilen;
+  if (pkt[9] != 17)
+    {
+      fprintf(siplog, "\tNot UDP (protocol %02x)\n", pkt[9]);
+      return;
+    }
+  if (get_u16(pkt+6) & 0x3fff)
+    {
+      fprintf(siplog, "\tFragmented\n");
+      return;
+    }
+  unsigned int saddr = get_u32(pkt+12);
+  unsigned int daddr = get_u32(pkt+16);
+  len -= hlen;
+  pkt += hlen;
+
+  /* Parse UDP header */
+  if (len < 8)
+    {
+      fprintf(siplog, "\tTruncated UDP header\n");
+      return;
+    }
+  unsigned int sport = get_u16(pkt);
+  unsigned int dport = get_u16(pkt+2);
+  unsigned int ulen = get_u16(pkt+4);
+  if (len < ulen)
+    {
+      fprintf(siplog, "\tTruncated UDP data (have %d, want %d)\n", len, ulen);
+      return;
+    }
+  len = ulen-8;
+  pkt += 8;
+
+  fprintf(siplog, "\t%08x:%d -> %08x:%d (%d bytes)\n", saddr, sport, daddr, dport, len);
+  sip_parse_header(pkt, len);
+}
+
+static int sip_output(ulog_iret_t *res __attribute__((unused)))
+{
+  if (!KEY_OK(K_RAW_PKT, RAW) ||
+      !KEY_OK(K_RAW_PKTLEN, UINT32) ||
+      !KEY_OK(K_OOB_TIME_SEC, UINT32) ||
+      !KEY_OK(K_OOB_TIME_USEC, UINT32))
+    {
+      ulogd_log(ULOGD_ERROR, "SIP: Mandatory keys missing\n");
+      return 1;
+    }
+
+  if (!KEY_OK(K_OOB_PREFIX, STRING) || strcasecmp(KEY_VAL(K_OOB_PREFIX).ptr, "phone"))
+    {
+      fprintf(siplog, "(non-phone packet received)\n");
+      return 1;
+    }
+
+  byte *pkt = KEY_VAL(K_RAW_PKT).ptr;
+  unsigned int len = KEY_VAL(K_RAW_PKTLEN).ui32;
+  time_t t = KEY_VAL(K_OOB_TIME_SEC).ui32;
+  struct tm *tm = localtime(&t);
+  if (!tm)
+    fprintf(siplog, "<#%d> ", (int)t);
+  else
+    fprintf(siplog, "%04d-%02d-%02d %02d:%02d:%02d.%06d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
+      tm->tm_hour, tm->tm_min, tm->tm_sec, KEY_VAL(K_OOB_TIME_USEC).ui32);
+  fprintf(siplog, " [%d bytes]\n", len);
+
+  sip_parse_packet(pkt, len);
+
+  fflush(siplog);
+  return 0;
+}
+
+/*** Housekeeping ***/
+
+static int sip_init(void)
+{
+  if (config_parse_file("SIP", &sip_cf_log))
+    {
+      ulogd_log(ULOGD_ERROR, "SIP: Parsing of configuration file failed\n");
+      return 1;
+    }
+  ulogd_log(ULOGD_INFO, "Initializing SIP plugin, logging to %s\n", sip_cf_log.u.string);
+  siplog = fopen(sip_cf_log.u.string, "a");
+  if (!siplog)
+    {
+      ulogd_log(ULOGD_ERROR, "Cannot open %s: %s", sip_cf_log.u.string, strerror(errno));
+      return 1;
+    }
+  return 0;
+}
+
+static void sip_fini(void)
+{
+  // Logging does not work here
+  // ulogd_log(ULOGD_INFO, "siplog fini\n");
+  fclose(siplog);
+}
+
+static void sip_signal(int sig)
+{
+  if (sig == SIGHUP)
+    {
+      // FIXME: Should reopen the log file here
+    }
+}
+
+static ulog_output_t sip_op = {
+  .name = "siplog",
+  .init = sip_init,
+  .fini = sip_fini,
+  .output = sip_output,
+  .signal = sip_signal
+};
+
+static void __attribute__((constructor)) constr(void)
+{
+  // All logging goes to stderr instead of the log file
+  // ulogd_log(ULOGD_INFO, "Registered siplog plugin\n");
+  if (!init_keys())
+    register_output(&sip_op);
+}