--- /dev/null
+/*
+ * 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);
+}