From: Martin Mares Date: Sat, 5 Jan 2008 23:17:50 +0000 (+0100) Subject: Initial commit X-Git-Tag: v1.0~5 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=72f55bbef68fb9030794f3ef99a6d41bbedee5c4;p=siplog.git Initial commit --- 72f55bbef68fb9030794f3ef99a6d41bbedee5c4 diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..81d36a8 --- /dev/null +++ b/siplog.c @@ -0,0 +1,317 @@ +/* + * A Simple SIP Logging Plugin for ulogd + * + * (c) 2008 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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= 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); +}