#include <stdio.h>
#include <errno.h>
#include <string.h>
+#include <stdarg.h>
#include <signal.h>
#include <time.h>
#include <alloca.h>
#include <ulogd/conffile.h>
typedef unsigned char byte;
+typedef unsigned int uns;
+
+static inline int is_white(int c)
+{
+ return (c == ' ' || c == '\t');
+}
/*** Configuration ***/
/*** Unaligned access ***/
-static unsigned int get_u16(byte *p)
+static uns get_u16(byte *p)
{
return (p[0] << 8) | p[1];
}
-static unsigned int get_u32(byte *p)
+static uns get_u32(byte *p)
{
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
static struct {
char *name;
- unsigned int id;
+ uns id;
} keys[] = {
{ "raw.pkt", 0 },
{ "raw.pktlen", 0 },
static int init_keys(void)
{
- for (unsigned int i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
+ for (uns 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);
#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;
+/*** Representation of packets ***/
+
+struct pkt {
+ time_t time_sec; // Arrival time
+ uns time_usec;
+ char time[64];
+ byte *raw; // Raw packet
+ uns raw_len;
+ uns out_p;
+ byte *ip; // Inside IP
+ uns ip_len;
+ uns src_addr, dst_addr;
+ byte *udp; // Inside UDP
+ uns udp_len;
+ uns src_port, dst_port;
+ char *sip_rr; // Decoded SIP
+ struct hdr *sip_hdr;
+};
struct hdr {
struct hdr *next;
char *name, *value;
};
-static void sip_parse_obj(char *rr, struct hdr *headers)
+/*** Logging ***/
+
+static FILE *siplog;
+static int verbose = 1;
+
+static void debug(char *fmt, ...)
{
+ if (!verbose)
+ return;
+ va_list args;
+ va_start(args, fmt);
+ fputc('\t', siplog);
+ vfprintf(siplog, fmt, args);
+ fputc('\n', siplog);
+ va_end(args);
+}
+
+static int error(struct pkt *pkt, char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(siplog, "%s ", pkt->time);
+ vfprintf(siplog, fmt, args);
+ fputc('\n', siplog);
+ va_end(args);
+ return 0;
}
-static void sip_parse_header(byte *pkt, unsigned int len)
+static int parse_rr(struct pkt *pkt)
{
- char *p = (char*) pkt;
- char *end = p + len;
- struct hdr *headers = NULL;
+ char *rr = pkt->sip_rr;
+ if (!strncmp(rr, "SIP/", 4))
+ {
+ // Reply
+ while (*rr && !is_white(*rr))
+ rr++;
+ while (is_white(*rr))
+ rr++;
+ }
+ else
+ {
+ // Request
+ }
+ return 1;
+}
-#if 0
- for (unsigned int i=0; i<len; i++)
- fputc(p[i], siplog);
- fprintf(siplog, "----\n");
-#endif
+static int parse_sip(struct pkt *pkt)
+{
+ char *p = (char*) pkt->udp;
+ char *end = p + pkt->udp_len;
+ pkt->sip_hdr = NULL;
// Parse the request/reply line first
- char *rr = p;
+ pkt->sip_rr = p;
while (p < end && *p != '\r' && *p != '\n')
p++;
if (p >= end)
- {
- fprintf(siplog, "\tTruncated 1st line\n");
- return;
- }
+ return error(pkt, "Truncated 1st line");
if (*p == '\r')
*p++ = 0;
if (p < end && *p == '\n')
*p++ = 0;
- fprintf(siplog, "\t>>> %s\n", rr);
+ debug(">>> %s", pkt->sip_rr);
// Slurp the other header lines
for (;;)
for (;;)
{
if (p >= end)
- {
- fprintf(siplog, "\tTruncated SIP headers\n");
- return;
- }
+ return error(pkt, "Truncated SIP headers");
if (*p == '\r')
p++;
else if (*p == '\n')
{
p++;
- if (p < end && (*p == ' ' || *p == '\t'))
+ if (p < end && is_white(*p))
;
else
{
}
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");
+ {
+ error(pkt, "Malformed header line");
continue;
}
*sep = 0;
- for (char *p=sep; p>start && (p[-1] == ' ' || p[-1] == '\t');)
+ for (char *p=sep; p>start && is_white(p[-1]);)
*--p = 0;
- while (++sep < end && (*sep == ' ' || *sep == '\t'))
+ while (++sep < end && is_white(*sep))
*sep = 0;
- fprintf(siplog, "\t%s: %s\n", start, sep);
+ debug("\t%s: %s", start, sep);
struct hdr *h = alloca(sizeof(*h));
- h->next = headers;
- headers = h;
+ h->next = pkt->sip_hdr;
+ pkt->sip_hdr = h;
h->name = start;
h->value = sep;
}
- sip_parse_obj(rr, headers);
+ return parse_rr(pkt);
}
-static void sip_parse_packet(byte *pkt, unsigned int len)
+static int parse_udp(struct pkt *pkt)
{
- /* 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;
+ byte *p = pkt->ip;
+ if (pkt->ip_len < 8)
+ return error(pkt, "Truncated UDP header");
+ pkt->src_port = get_u16(p);
+ pkt->dst_port = get_u16(p+2);
+ uns ulen = get_u16(p+4);
+ if (ulen < 8)
+ return error(pkt, "Invalid UDP length (%d)", ulen);
+ if (ulen > pkt->ip_len)
+ return error(pkt, "Truncated UDP data (have %d, want %d)", pkt->ip_len, ulen);
+ pkt->udp = p + 8;
+ pkt->udp_len = ulen - 8;
+ debug("%08x:%d -> %08x:%d (%d bytes, out_p=%d)", pkt->src_addr, pkt->src_port, pkt->dst_addr, pkt->dst_port, pkt->out_p);
+ return parse_sip(pkt);
+}
- fprintf(siplog, "\t%08x:%d -> %08x:%d (%d bytes)\n", saddr, sport, daddr, dport, len);
- sip_parse_header(pkt, len);
+static int parse_ip(struct pkt *pkt)
+{
+ byte *p = pkt->raw;
+ if (pkt->raw_len < 20)
+ return error(pkt, "Very truncated IP header");
+ uns ipver = p[0] >> 4;
+ if (ipver != 4)
+ return error(pkt, "We don't speak IPv%d", ipver);
+ uns hlen = (p[0] & 0x0f)*4;
+ if (pkt->raw_len < hlen)
+ return error(pkt, "Truncated IP header (want %d)", hlen);
+ uns ilen = get_u16(p+2);
+ if (ilen < hlen)
+ return error(pkt, "Invalid IP length (have %d, want at least %d for header)", ilen, hlen);
+ if (ilen > pkt->raw_len)
+ return error(pkt, "Truncated (have %d, want %d)", pkt->raw_len, ilen);
+ if (p[9] != 17)
+ return error(pkt, "Not UDP (protocol %02x)", p[9]);
+ if (get_u16(p+6) & 0x3fff)
+ return error(pkt, "Fragmented");
+ pkt->ip = p + hlen;
+ pkt->ip_len = ilen - hlen;
+ pkt->src_addr = get_u32(p+12);
+ pkt->dst_addr = get_u32(p+16);
+ return parse_udp(pkt);
}
+/*** Interface to ulogd ***/
+
static int sip_output(ulog_iret_t *res __attribute__((unused)))
{
+ struct pkt pkt;
+
+ char *prefix = (KEY_OK(K_OOB_PREFIX, STRING) ? KEY_VAL(K_OOB_PREFIX).ptr : "?");
+ if (!strcasecmp(prefix, "phone-in"))
+ pkt.out_p = 0;
+ else if (!strcasecmp(prefix, "phone-out"))
+ pkt.out_p = 1;
+ else
+ {
+ if (verbose > 1)
+ fprintf(siplog, "(non-phone packet received, prefix=%s)\n", prefix);
+ return 0;
+ }
+
if (!KEY_OK(K_RAW_PKT, RAW) ||
!KEY_OK(K_RAW_PKTLEN, UINT32) ||
!KEY_OK(K_OOB_TIME_SEC, 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);
+ pkt.raw = KEY_VAL(K_RAW_PKT).ptr;
+ pkt.raw_len = KEY_VAL(K_RAW_PKTLEN).ui32;
+ pkt.time_sec = KEY_VAL(K_OOB_TIME_SEC).ui32;
+ pkt.time_usec = KEY_VAL(K_OOB_TIME_USEC).ui32;
+ struct tm *tm = localtime(&pkt.time_sec);
if (!tm)
- fprintf(siplog, "<#%d> ", (int)t);
+ sprintf(pkt.time, "<#%d.%06d> ", (int)pkt.time_sec, pkt.time_usec);
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);
+ sprintf(pkt.time, "%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, pkt.time_usec);
+ if (verbose)
+ fprintf(siplog, "\t%s\n", pkt.time);
+ parse_ip(&pkt);
fflush(siplog);
return 0;
}