]> mj.ucw.cz Git - siplog.git/blob - siplog.c
Tag releases automatically
[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 <stdlib.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <stdarg.h>
12 #include <signal.h>
13 #include <time.h>
14 #include <alloca.h>
15 #include <ulogd/ulogd.h>
16 #include <ulogd/conffile.h>
17
18 typedef unsigned char byte;
19 typedef unsigned int uns;
20
21 /*** Configuration ***/
22
23 static config_entry_t sip_cf_mail_to = {
24   .next = NULL,
25   .key = "mailto",
26   .type = CONFIG_TYPE_STRING,
27   .options = CONFIG_OPT_NONE,
28   .u.string = ""
29 };
30
31 static config_entry_t sip_cf_debug_log = {
32   .next = &sip_cf_mail_to,
33   .key = "debuglog",
34   .type = CONFIG_TYPE_STRING,
35   .options = CONFIG_OPT_NONE,
36   .u.string = ""
37 };
38
39 static config_entry_t sip_cf_log = {
40   .next = &sip_cf_debug_log,
41   .key = "file",
42   .type = CONFIG_TYPE_STRING,
43   .options = CONFIG_OPT_NONE,
44   .u.string = "/var/log/ulog/sip.log"
45 };
46
47 /*** Unaligned access ***/
48
49 static uns get_u16(byte *p)
50 {
51   return (p[0] << 8) | p[1];
52 }
53
54 static uns get_u32(byte *p)
55 {
56   return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
57 }
58
59 /*** Keys ***/
60
61 static struct {
62   char *name;
63   uns id;
64 } keys[] = {
65   { "raw.pkt", 0 },
66   { "raw.pktlen", 0 },
67   { "oob.time.sec", 0 },
68   { "oob.time.usec", 0 },
69   { "oob.prefix", 0 },
70 };
71
72 enum {
73   K_RAW_PKT = 0,
74   K_RAW_PKTLEN,
75   K_OOB_TIME_SEC,
76   K_OOB_TIME_USEC,
77   K_OOB_PREFIX,
78 };
79
80 static int init_keys(void)
81 {
82   for (uns i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
83     if (!(keys[i].id = keyh_getid(keys[i].name)))
84       {
85         ulogd_log(ULOGD_ERROR, "SIP: Unable to resolve key %s\n", keys[i].name);
86         return 1;
87       }
88   return 0;
89 }
90
91 #define KEY_H(i) ulogd_keyh[keys[i].id].interp->result[ulogd_keyh[keys[i].id].offset]
92 #define KEY_VAL(i) KEY_H(i).value
93 #define KEY_FLAGS(i) KEY_H(i).flags
94 #define KEY_TYPE(i) KEY_H(i).type
95 #define KEY_OK(i,t) ((KEY_FLAGS(i) & ULOGD_RETF_VALID) && KEY_TYPE(i) == ULOGD_RET_##t)
96
97 /*** Representation of packets ***/
98
99 struct pkt {
100   time_t time_sec;              // Arrival time
101   uns time_usec;
102   char time[64];
103   byte *raw;                    // Raw packet
104   uns raw_len;
105   uns out_p;
106   byte *ip;                     // Inside IP
107   uns ip_len;
108   uns src_addr, dst_addr;
109   byte *udp;                    // Inside UDP
110   uns udp_len;
111   uns src_port, dst_port;
112   char *sip_rr;                 // Decoded SIP
113   struct hdr *sip_hdr;
114   char *sip_method;
115   char *sip_uri;
116   int sip_status;
117   char *sip_msg;
118   struct call *sip_call;
119   char *sip_seq;
120 };
121
122 struct hdr {
123   struct hdr *next;
124   char *name, *value;
125 };
126
127 static char *find_hdr(struct pkt *pkt, char *name)
128 {
129   for (struct hdr *h=pkt->sip_hdr; h; h=h->next)
130     if (!strcasecmp(h->name, name))
131       return h->value;
132   return NULL;
133 }
134
135 static char *find_hdr2(struct pkt *pkt, char *name, char *alt)
136 {
137   return find_hdr(pkt, name) ? : find_hdr(pkt, alt);
138 }
139
140 /*** Logging ***/
141
142 static FILE *siplog, *debuglog;
143
144 static void debug(char *fmt, ...)
145 {
146   if (!debuglog)
147     return;
148   va_list args;
149   va_start(args, fmt);
150   fputc('\t', debuglog);
151   vfprintf(debuglog, fmt, args);
152   fputc('\n', debuglog);
153   va_end(args);
154 }
155
156 static int error(struct pkt *pkt, char *fmt, ...)
157 {
158   va_list args;
159   va_start(args, fmt);
160   fprintf(siplog, "%s ERR: ", pkt->time);
161   vfprintf(siplog, fmt, args);
162   fputc('\n', siplog);
163   va_end(args);
164   return 0;
165 }
166
167 /*** Auxillary routines ***/
168
169 static inline int is_white(int c)
170 {
171   return (c == ' ' || c == '\t');
172 }
173
174 static char *skip_ws(char *c)
175 {
176   while (is_white(*c))
177     c++;
178   return c;
179 }
180
181 static char *skip_nonws(char *c)
182 {
183   while (*c && !is_white(*c))
184     c++;
185   return c;
186 }
187
188 /*** The parser ***/
189
190 #define CALL_ID_LEN 63
191 #define NUM_LEN 63
192 #define CSEQ_LEN 63
193
194 enum call_state {
195   CALL_IDLE,
196   CALL_DIALING,
197   CALL_CONNECTED,
198   CALL_BYE,
199 };
200
201 enum call_flags {
202   FLAG_RING = 1,
203   FLAG_ANSWERED = 2,
204   FLAG_REDIRECT = 4,
205 };
206
207 struct call {
208   struct call *next;
209   char call_id[CALL_ID_LEN+1];
210   enum call_state state;
211   uns out_p;
212   char active_seq[CSEQ_LEN+1];
213   char src_party[NUM_LEN+1];
214   char dst_party[NUM_LEN+1];
215   uns flags;
216 };
217 static struct call *call_list;
218
219 static void log_call(struct pkt *pkt, struct call *c)
220 {
221   fprintf(siplog, "%s: CALL %s <%s> -> <%s>", pkt->time, (c->out_p ? "OUT" : "IN"), c->src_party, c->dst_party);
222   if (c->flags & FLAG_ANSWERED)
223     fprintf(siplog, " ANS");
224   else if (c->flags & FLAG_RING)
225     fprintf(siplog, " RING");
226   if (c->flags & FLAG_REDIRECT)
227     fprintf(siplog, " REDIR");
228   if (pkt->sip_status >= 300)
229     fprintf(siplog, " ERR:%s", pkt->sip_msg);
230   else
231     fprintf(siplog, " OK");
232   fputc('\n', siplog);
233
234   if (sip_cf_mail_to.u.string[0])
235     {
236       char cmd[256];
237       sprintf(cmd, "mail -s 'SIP CALL' %s", sip_cf_mail_to.u.string);
238       debug("Running %s", cmd);
239       FILE *m = popen(cmd, "w");
240       if (m)
241         {
242           fprintf(m, "Finished-At: %s\n", pkt->time);
243           fprintf(m, "Direction: %s\n", (c->out_p ? "OUT" : "IN"));
244           fprintf(m, "From: %s\n", c->src_party);
245           fprintf(m, "To: %s\n", c->dst_party);
246           fprintf(m, "Flags:%s%s%s\n",
247             ((c->flags & FLAG_ANSWERED) ? " ANS" : ""),
248             ((c->flags & FLAG_RING) ? " RING" : ""),
249             ((c->flags & FLAG_REDIRECT) ? " REDIR" : ""));
250           fprintf(m, "Status: %s\n", pkt->sip_msg);
251           fclose(m);
252         }
253       else
254         error(pkt, "Cannot execute `%s': %m", cmd);
255     }
256 }
257
258 static void extract_number(char *buf, char *h)
259 {
260   if (!h)
261     h = "???";
262   char *c;
263   // Multiple addresses => use the first one
264   if (c = strchr(h, ','))
265     *c = 0;
266   // Tagged parameters => drop them
267   if (c = strchr(h, ';'))
268     *c = 0;
269   // Quoted part => prefer it
270   if (c = strchr(h, '<'))
271     {
272       h = c+1;
273       if (c = strchr(h, '>'))
274         *c = 0;
275     }
276   // Skip URL scheme
277   if (c = strchr(h, ':'))
278     h = c+1;
279   // Use only numeric characters, * and #
280   char *d = buf;
281   while (d < buf + NUM_LEN)
282     {
283       if (*h >= '0' && *h <= '9' || *h == '*')
284         *d++ = *h++;
285       else if (*h == '%' && h[1] == '2' && h[2] == '3')
286         {
287           *d++ = '#';
288           h += 3;
289         }
290       else
291         break;
292     }
293   *d = 0;
294 }
295
296 static int parse_request(struct pkt *pkt)
297 {
298   struct call *c = pkt->sip_call;
299   if (!strcmp(pkt->sip_seq, c->active_seq))
300     {
301       debug("Retransmitted packet");
302       return 1;
303     }
304   if (!strcasecmp(pkt->sip_method, "INVITE"))
305     {
306       if (c->state != CALL_IDLE)
307         return error(pkt, "re-INVITE not supported yet");
308       c->state = CALL_DIALING;
309       c->out_p = pkt->out_p;
310       strcpy(c->active_seq, pkt->sip_seq);
311       extract_number(c->src_party, find_hdr2(pkt, "From", "f"));
312       extract_number(c->dst_party, find_hdr2(pkt, "To", "t"));
313     }
314   else if (!strcasecmp(pkt->sip_method, "BYE"))
315     {
316       if (c->state != CALL_CONNECTED)
317         {
318           c->state = CALL_IDLE;
319           return error(pkt, "Unexpected BYE");
320         }
321       c->state = CALL_BYE;
322       strcpy(c->active_seq, pkt->sip_seq);
323     }
324   return 1;
325 }
326
327 static int parse_reply(struct pkt *pkt)
328 {
329   struct call *c = pkt->sip_call;
330   if (strcmp(pkt->sip_seq, c->active_seq))
331     {
332       debug("Reply not associated to active request %s", c->active_seq);
333       return 1;
334     }
335
336   uns stat = pkt->sip_status;
337   if (stat >= 200)
338     c->active_seq[0] = 0;
339
340   if (c->state == CALL_DIALING)
341     {
342       if (stat < 100)
343         ;
344       else if (stat < 200)
345         {
346           // Provisional responses
347           if (stat == 180)
348             c->flags |= FLAG_RING;
349         }
350       else if (stat < 300)
351         {
352           // Success
353           c->state = CALL_CONNECTED;
354           c->flags |= FLAG_ANSWERED;
355         }
356       else if (stat < 400)
357         {
358           // Redirect
359           c->flags |= FLAG_REDIRECT;
360           log_call(pkt, c);
361           c->state = CALL_IDLE;
362         }
363       else
364         {
365           // Failure of some sort
366           switch (stat)
367             {
368               case 408:         // Timeout
369               case 404:         // Not found
370               case 403:         // Forbidden
371               case 480:         // Tmp unavailable
372               case 486:         // Busy here
373               case 487:         // Cancelled
374               case 600:         // Busy everywhere
375               case 603:         // Decline
376               case 604:         // Does not exist
377               case 606:         // Unaccaptable
378                 log_call(pkt, c);
379             }
380           c->state = CALL_IDLE;
381           // We do not reset flags on failures, because another INVITE may be sent for the same call and we wish to remember redirects etc.
382         }
383     }
384   else if (c->state == CALL_BYE)
385     {
386       if (stat >= 200)
387         {
388           log_call(pkt, c);
389           c->state = CALL_IDLE;
390         }
391     }
392   return 1;
393 }
394
395 static int parse_call(struct pkt *pkt)
396 {
397   char *id = find_hdr2(pkt, "Call-ID", "i");
398   if (!id)
399     return error(pkt, "Missing Call-ID");
400   char *p = skip_nonws(id);
401   *p = 0;
402   if (!*id)
403     return error(pkt, "Empty Call-ID");
404   if (strlen(id) > CALL_ID_LEN)
405     return error(pkt, "Call-ID too long");
406
407   pkt->sip_seq = find_hdr(pkt, "CSeq");
408   if (!pkt->sip_seq)
409     return error(pkt, "Missing CSeq");
410   if (strlen(pkt->sip_seq) > CSEQ_LEN)
411     return error(pkt, "CSeq too long");
412
413   struct call *c;
414   for (c=call_list; c && strcasecmp(c->call_id, id); c=c->next)
415     ;
416   if (!c)
417     {
418       if (!pkt->sip_method)
419         return error(pkt, "Cannot match reply to an existing call");
420       debug("Creating new call <%s>", id);
421       c = malloc(sizeof(struct call));
422       if (!c)
423         return error(pkt, "Out of memory");
424       memset(c, 0, sizeof(*c));
425       c->next = call_list;
426       call_list = c;
427       strcpy(c->call_id, id);
428       c->state = CALL_IDLE;
429     }
430   else
431     debug("Continuing call <%s> in state %d", id, c->state);
432   pkt->sip_call = c;
433   // FIXME: We should time out the calls
434
435   if (pkt->sip_method)
436     return parse_request(pkt);
437   else
438     return parse_reply(pkt);
439 }
440
441 static int parse_rr(struct pkt *pkt)
442 {
443   char *rr = pkt->sip_rr;
444   if (!strncmp(rr, "SIP/", 4))
445     {
446       // Reply
447       rr = skip_ws(skip_nonws(rr));
448       if (!*rr)
449         return error(pkt, "Truncated SIP response");
450       pkt->sip_msg = rr;
451       pkt->sip_status = atol(rr);
452       pkt->sip_method = NULL;
453       pkt->sip_uri = NULL;
454       debug("SIP status %d (%s)", pkt->sip_status, pkt->sip_msg);
455     }
456   else
457     {
458       // Request
459       pkt->sip_msg = NULL;
460       pkt->sip_status = -1;
461       pkt->sip_method = rr;
462       rr = skip_nonws(rr);
463       if (!*rr)
464         return error(pkt, "Missing URI in SIP request");
465       *rr++ = 0;
466       rr = skip_ws(rr);
467       if (!*rr)
468         return error(pkt, "Missing URI in SIP request");
469       pkt->sip_uri = rr;
470       rr = skip_nonws(rr);
471       *rr = 0;
472       debug("SIP request %s on %s", pkt->sip_method, pkt->sip_uri);
473     }
474   return parse_call(pkt);
475 }
476
477 static int parse_sip(struct pkt *pkt)
478 {
479   char *p = (char*) pkt->udp;
480   char *end = p + pkt->udp_len;
481   pkt->sip_rr = NULL;
482   pkt->sip_hdr = NULL;
483
484   // Parse the request/reply line first
485   pkt->sip_rr = p;
486   while (p < end && *p != '\r' && *p != '\n')
487     p++;
488   if (p >= end)
489     return error(pkt, "Truncated 1st line");
490   if (*p == '\r')
491     *p++ = 0;
492   if (p < end && *p == '\n')
493     *p++ = 0;
494   debug(">>> %s", pkt->sip_rr);
495
496   // Slurp the other header lines and sanitize them
497   for (;;)
498     {
499       char *start = p;
500       char *w = start;
501       for (;;)
502         {
503           if (p >= end)
504             return error(pkt, "Truncated SIP headers");
505           if (*p == '\r')
506             p++;
507           else if (*p == '\n')
508             {
509               p++;
510               if (p < end && is_white(*p) && pkt->sip_rr)
511                 ;
512               else
513                 {
514                   *w++ = 0;
515                   break;
516                 }
517             }
518           else if (*p > 0x20 && *p != 0x7f || *p < 0)
519             *w++ = *p++;
520           else if (is_white(*p))
521             *w++ = ' ', p++;
522           else
523             *w++ = '?', p++;
524         }
525       if (!start[0])
526         break;
527       // Got a line, parse it
528       if (!pkt->sip_rr)
529         {
530           pkt->sip_rr = start;
531           debug(">>> %s", pkt->sip_rr);
532           continue;
533         }
534       char *sep = strchr(start, ':');
535       if (!*sep)
536         {
537           error(pkt, "Malformed header line");
538           continue;
539         }
540       *sep = 0;
541       for (char *p=sep; p>start && is_white(p[-1]);)
542         *--p = 0;
543       while (++sep < end && is_white(*sep))
544         *sep = 0;
545       debug("\t%s: %s", start, sep);
546       struct hdr *h = alloca(sizeof(*h));
547       h->next = pkt->sip_hdr;
548       pkt->sip_hdr = h;
549       h->name = start;
550       h->value = sep;
551     }
552   if (!pkt->sip_rr)
553     return error(pkt, "Missing SIP header");
554
555   return parse_rr(pkt);
556 }
557
558 static int parse_udp(struct pkt *pkt)
559 {
560   byte *p = pkt->ip;
561   if (pkt->ip_len < 8)
562     return error(pkt, "Truncated UDP header");
563   pkt->src_port = get_u16(p);
564   pkt->dst_port = get_u16(p+2);
565   uns ulen = get_u16(p+4);
566   if (ulen < 8)
567     return error(pkt, "Invalid UDP length (%d)", ulen);
568   if (ulen > pkt->ip_len)
569     return error(pkt, "Truncated UDP data (have %d, want %d)", pkt->ip_len, ulen);
570   pkt->udp = p + 8;
571   pkt->udp_len = ulen - 8;
572   debug("%08x:%d -> %08x:%d (%d bytes, out_p=%d)", pkt->src_addr, pkt->src_port, pkt->dst_addr, pkt->dst_port, pkt->udp_len, pkt->out_p);
573   return parse_sip(pkt);
574 }
575
576 static int parse_ip(struct pkt *pkt)
577 {
578   byte *p = pkt->raw;
579   if (pkt->raw_len < 20)
580     return error(pkt, "Very truncated IP header");
581   uns ipver = p[0] >> 4;
582   if (ipver != 4)
583     return error(pkt, "We don't speak IPv%d", ipver);
584   uns hlen = (p[0] & 0x0f)*4;
585   if (pkt->raw_len < hlen)
586     return error(pkt, "Truncated IP header (want %d)", hlen);
587   uns ilen  = get_u16(p+2);
588   if (ilen < hlen)
589     return error(pkt, "Invalid IP length (have %d, want at least %d for header)", ilen, hlen);
590   if (ilen > pkt->raw_len)
591     return error(pkt, "Truncated (have %d, want %d)", pkt->raw_len, ilen);
592   if (p[9] != 17)
593     return error(pkt, "Not UDP (protocol %02x)", p[9]);
594   if (get_u16(p+6) & 0x3fff)
595     return error(pkt, "Fragmented");
596   pkt->ip = p + hlen;
597   pkt->ip_len = ilen - hlen;
598   pkt->src_addr = get_u32(p+12);
599   pkt->dst_addr = get_u32(p+16);
600   return parse_udp(pkt);
601 }
602
603 /*** Interface to ulogd ***/
604
605 static int sip_output(ulog_iret_t *res __attribute__((unused)))
606 {
607   struct pkt pkt;
608
609   if (!siplog)
610     return 0;
611
612   char *prefix = (KEY_OK(K_OOB_PREFIX, STRING) ? KEY_VAL(K_OOB_PREFIX).ptr : "?");
613   if (!strcasecmp(prefix, "phone-in"))
614     pkt.out_p = 0;
615   else if (!strcasecmp(prefix, "phone-out"))
616     pkt.out_p = 1;
617   else
618     {
619       // fprintf(debuglog, "(non-phone packet received, prefix=%s)\n", prefix);
620       return 0;
621     }
622
623   if (!KEY_OK(K_RAW_PKT, RAW) ||
624       !KEY_OK(K_RAW_PKTLEN, UINT32) ||
625       !KEY_OK(K_OOB_TIME_SEC, UINT32) ||
626       !KEY_OK(K_OOB_TIME_USEC, UINT32))
627     {
628       ulogd_log(ULOGD_ERROR, "SIP: Mandatory keys missing\n");
629       return 1;
630     }
631   pkt.raw = KEY_VAL(K_RAW_PKT).ptr;
632   pkt.raw_len = KEY_VAL(K_RAW_PKTLEN).ui32;
633   pkt.time_sec = KEY_VAL(K_OOB_TIME_SEC).ui32;
634   pkt.time_usec = KEY_VAL(K_OOB_TIME_USEC).ui32;
635   struct tm *tm = localtime(&pkt.time_sec);
636   if (!tm)
637     sprintf(pkt.time, "<#%d.%06d> ", (int)pkt.time_sec, pkt.time_usec);
638   else
639     sprintf(pkt.time, "%04d-%02d-%02d %02d:%02d:%02d.%06d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
640       tm->tm_hour, tm->tm_min, tm->tm_sec, pkt.time_usec);
641   if (debuglog)
642     fprintf(debuglog, "%s\n", pkt.time);
643
644   parse_ip(&pkt);
645   fflush(siplog);
646   if (debuglog)
647     fflush(debuglog);
648   return 0;
649 }
650
651 /*** Housekeeping ***/
652
653 static int sip_init(void)
654 {
655   if (config_parse_file("SIP", &sip_cf_log))
656     {
657       ulogd_log(ULOGD_ERROR, "SIP: Parsing of configuration file failed\n");
658       return 1;
659     }
660   ulogd_log(ULOGD_INFO, "Initializing SIP plugin, logging to %s\n", sip_cf_debug_log.u.string);
661   siplog = fopen(sip_cf_log.u.string, "a");
662   if (!siplog)
663     {
664       ulogd_log(ULOGD_ERROR, "Cannot open %s: %s", sip_cf_log.u.string, strerror(errno));
665       return 1;
666     }
667   if (sip_cf_debug_log.u.string[0])
668     {
669       debuglog = fopen(sip_cf_debug_log.u.string, "a");
670       if (!debuglog)
671         {
672           ulogd_log(ULOGD_ERROR, "Cannot open %s: %s", sip_cf_debug_log.u.string, strerror(errno));
673           return 1;
674         }
675     }
676   return 0;
677 }
678
679 static void sip_fini(void)
680 {
681   // Logging does not work here
682   // ulogd_log(ULOGD_INFO, "siplog fini\n");
683   fclose(siplog);
684   fclose(debuglog);
685   siplog = debuglog = NULL;
686 }
687
688 static void sip_signal(int sig)
689 {
690   if (sig == SIGHUP)
691     {
692       // FIXME: Should reopen the log files here
693     }
694 }
695
696 static ulog_output_t sip_op = {
697   .name = "siplog",
698   .init = sip_init,
699   .fini = sip_fini,
700   .output = sip_output,
701   .signal = sip_signal
702 };
703
704 static void __attribute__((constructor)) constr(void)
705 {
706   // All logging goes to stderr instead of the log file
707   // ulogd_log(ULOGD_INFO, "Registered siplog plugin\n");
708   if (!init_keys())
709     register_output(&sip_op);
710 }