]> mj.ucw.cz Git - siplog.git/blob - siplog.c
Initial commit
[siplog.git] / siplog.c
1 /*
2  *      A Simple SIP Logging Plugin for ulogd
3  *
4  *      (c) 2008 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <stdio.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <signal.h>
11 #include <time.h>
12 #include <alloca.h>
13 #include <ulogd/ulogd.h>
14 #include <ulogd/conffile.h>
15
16 typedef unsigned char byte;
17
18 /*** Configuration ***/
19
20 static config_entry_t sip_cf_log = {
21   .next = NULL,
22   .key = "file",
23   .type = CONFIG_TYPE_STRING,
24   .options = CONFIG_OPT_NONE,
25   .u.string = "/var/log/ulog/sip.log"
26 };
27
28 /*** Unaligned access ***/
29
30 static unsigned int get_u16(byte *p)
31 {
32   return (p[0] << 8) | p[1];
33 }
34
35 static unsigned int get_u32(byte *p)
36 {
37   return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
38 }
39
40 /*** Keys ***/
41
42 static struct {
43   char *name;
44   unsigned int id;
45 } keys[] = {
46   { "raw.pkt", 0 },
47   { "raw.pktlen", 0 },
48   { "oob.time.sec", 0 },
49   { "oob.time.usec", 0 },
50   { "oob.prefix", 0 },
51 };
52
53 enum {
54   K_RAW_PKT = 0,
55   K_RAW_PKTLEN,
56   K_OOB_TIME_SEC,
57   K_OOB_TIME_USEC,
58   K_OOB_PREFIX,
59 };
60
61 static int init_keys(void)
62 {
63   for (unsigned int i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
64     if (!(keys[i].id = keyh_getid(keys[i].name)))
65       {
66         ulogd_log(ULOGD_ERROR, "SIP: Unable to resolve key %s\n", keys[i].name);
67         return 1;
68       }
69   return 0;
70 }
71
72 #define KEY_H(i) ulogd_keyh[keys[i].id].interp->result[ulogd_keyh[keys[i].id].offset]
73 #define KEY_VAL(i) KEY_H(i).value
74 #define KEY_FLAGS(i) KEY_H(i).flags
75 #define KEY_TYPE(i) KEY_H(i).type
76 #define KEY_OK(i,t) ((KEY_FLAGS(i) & ULOGD_RETF_VALID) && KEY_TYPE(i) == ULOGD_RET_##t)
77
78 /*** Logging ***/
79
80 static FILE *siplog;
81
82 struct hdr {
83   struct hdr *next;
84   char *name, *value;
85 };
86
87 static void sip_parse_obj(char *rr, struct hdr *headers)
88 {
89 }
90
91 static void sip_parse_header(byte *pkt, unsigned int len)
92 {
93   char *p = (char*) pkt;
94   char *end = p + len;
95   struct hdr *headers = NULL;
96
97 #if 0
98   for (unsigned int i=0; i<len; i++)
99     fputc(p[i], siplog);
100   fprintf(siplog, "----\n");
101 #endif
102
103   // Parse the request/reply line first
104   char *rr = p;
105   while (p < end && *p != '\r' && *p != '\n')
106     p++;
107   if (p >= end)
108     {
109       fprintf(siplog, "\tTruncated 1st line\n");
110       return;
111     }
112   if (*p == '\r')
113     *p++ = 0;
114   if (p < end && *p == '\n')
115     *p++ = 0;
116   fprintf(siplog, "\t>>> %s\n", rr);
117
118   // Slurp the other header lines
119   for (;;)
120     {
121       char *start = p;
122       char *w = start;
123       for (;;)
124         {
125           if (p >= end)
126             {
127               fprintf(siplog, "\tTruncated SIP headers\n");
128               return;
129             }
130           if (*p == '\r')
131             p++;
132           else if (*p == '\n')
133             {
134               p++;
135               if (p < end && (*p == ' ' || *p == '\t'))
136                 ;
137               else
138                 {
139                   *w++ = 0;
140                   break;
141                 }
142             }
143           else
144             *w++ = *p++;
145         }
146       if (!start[0])
147         break;
148       // fprintf(siplog, "\tH: %s\n", start);
149       // Got a line, parse it
150       char *sep = strchr(start, ':');
151       if (!*sep)
152         {
153           fprintf(siplog, "\tMalformed header line\n");
154           continue;
155         }
156       *sep = 0;
157       for (char *p=sep; p>start && (p[-1] == ' ' || p[-1] == '\t');)
158         *--p = 0;
159       while (++sep < end && (*sep == ' ' || *sep == '\t'))
160         *sep = 0;
161       fprintf(siplog, "\t%s: %s\n", start, sep);
162       struct hdr *h = alloca(sizeof(*h));
163       h->next = headers;
164       headers = h;
165       h->name = start;
166       h->value = sep;
167     }
168
169   sip_parse_obj(rr, headers);
170 }
171
172 static void sip_parse_packet(byte *pkt, unsigned int len)
173 {
174   /* Parse IP header */
175   if (len < 20)
176     {
177       fprintf(siplog, "\tVery truncated IP header\n");
178       return;
179     }
180   unsigned int ipver = pkt[0] >> 4;
181   if (ipver != 4)
182     {
183       fprintf(siplog, "\tWe don't speak IPv%d\n", ipver);
184       return;
185     }
186   unsigned int hlen = (pkt[0] & 0x0f)*4;
187   if (len < hlen)
188     {
189       fprintf(siplog, "\tTruncated IP header (want %d)\n", hlen);
190       return;
191     }
192   unsigned int ilen = get_u16(pkt+2);
193   if (ilen > len)
194     {
195       fprintf(siplog, "\tTruncated (have %d, want %d)\n", len, ilen);
196       return;
197     }
198   len = ilen;
199   if (pkt[9] != 17)
200     {
201       fprintf(siplog, "\tNot UDP (protocol %02x)\n", pkt[9]);
202       return;
203     }
204   if (get_u16(pkt+6) & 0x3fff)
205     {
206       fprintf(siplog, "\tFragmented\n");
207       return;
208     }
209   unsigned int saddr = get_u32(pkt+12);
210   unsigned int daddr = get_u32(pkt+16);
211   len -= hlen;
212   pkt += hlen;
213
214   /* Parse UDP header */
215   if (len < 8)
216     {
217       fprintf(siplog, "\tTruncated UDP header\n");
218       return;
219     }
220   unsigned int sport = get_u16(pkt);
221   unsigned int dport = get_u16(pkt+2);
222   unsigned int ulen = get_u16(pkt+4);
223   if (len < ulen)
224     {
225       fprintf(siplog, "\tTruncated UDP data (have %d, want %d)\n", len, ulen);
226       return;
227     }
228   len = ulen-8;
229   pkt += 8;
230
231   fprintf(siplog, "\t%08x:%d -> %08x:%d (%d bytes)\n", saddr, sport, daddr, dport, len);
232   sip_parse_header(pkt, len);
233 }
234
235 static int sip_output(ulog_iret_t *res __attribute__((unused)))
236 {
237   if (!KEY_OK(K_RAW_PKT, RAW) ||
238       !KEY_OK(K_RAW_PKTLEN, UINT32) ||
239       !KEY_OK(K_OOB_TIME_SEC, UINT32) ||
240       !KEY_OK(K_OOB_TIME_USEC, UINT32))
241     {
242       ulogd_log(ULOGD_ERROR, "SIP: Mandatory keys missing\n");
243       return 1;
244     }
245
246   if (!KEY_OK(K_OOB_PREFIX, STRING) || strcasecmp(KEY_VAL(K_OOB_PREFIX).ptr, "phone"))
247     {
248       fprintf(siplog, "(non-phone packet received)\n");
249       return 1;
250     }
251
252   byte *pkt = KEY_VAL(K_RAW_PKT).ptr;
253   unsigned int len = KEY_VAL(K_RAW_PKTLEN).ui32;
254   time_t t = KEY_VAL(K_OOB_TIME_SEC).ui32;
255   struct tm *tm = localtime(&t);
256   if (!tm)
257     fprintf(siplog, "<#%d> ", (int)t);
258   else
259     fprintf(siplog, "%04d-%02d-%02d %02d:%02d:%02d.%06d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
260       tm->tm_hour, tm->tm_min, tm->tm_sec, KEY_VAL(K_OOB_TIME_USEC).ui32);
261   fprintf(siplog, " [%d bytes]\n", len);
262
263   sip_parse_packet(pkt, len);
264
265   fflush(siplog);
266   return 0;
267 }
268
269 /*** Housekeeping ***/
270
271 static int sip_init(void)
272 {
273   if (config_parse_file("SIP", &sip_cf_log))
274     {
275       ulogd_log(ULOGD_ERROR, "SIP: Parsing of configuration file failed\n");
276       return 1;
277     }
278   ulogd_log(ULOGD_INFO, "Initializing SIP plugin, logging to %s\n", sip_cf_log.u.string);
279   siplog = fopen(sip_cf_log.u.string, "a");
280   if (!siplog)
281     {
282       ulogd_log(ULOGD_ERROR, "Cannot open %s: %s", sip_cf_log.u.string, strerror(errno));
283       return 1;
284     }
285   return 0;
286 }
287
288 static void sip_fini(void)
289 {
290   // Logging does not work here
291   // ulogd_log(ULOGD_INFO, "siplog fini\n");
292   fclose(siplog);
293 }
294
295 static void sip_signal(int sig)
296 {
297   if (sig == SIGHUP)
298     {
299       // FIXME: Should reopen the log file here
300     }
301 }
302
303 static ulog_output_t sip_op = {
304   .name = "siplog",
305   .init = sip_init,
306   .fini = sip_fini,
307   .output = sip_output,
308   .signal = sip_signal
309 };
310
311 static void __attribute__((constructor)) constr(void)
312 {
313   // All logging goes to stderr instead of the log file
314   // ulogd_log(ULOGD_INFO, "Registered siplog plugin\n");
315   if (!init_keys())
316     register_output(&sip_op);
317 }