Correct utmp handling - Robert Davidson <puttputt@iaccess.ws>
[ppp.git] / pppd / plugins / pppoe / utils.c
1
2 /*
3  * utils.c - various utility functions used in pppoed.
4  *
5  * mostly stolen from ppp-2.3.10 by Marc Boucher <marc@mbsi.ca>
6  *
7  * Feb 18/2000 Made fully re-entrant (JHS)
8  *
9  * Copyright (c) 1999 The Australian National University.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms are permitted
13  * provided that the above copyright poe_notice and this paragraph are
14  * duplicated in all such forms and that any documentation,
15  * advertising materials, and other materials related to such
16  * distribution and use acknowledge that the software was developed
17  * by the Australian National University.  The name of the University
18  * may not be used to endorse or promote products derived from this
19  * software without specific prior written permission.
20  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
22  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23  */
24
25 #include <stdio.h>              /* stdio               */
26 #include <stdlib.h>             /* strtoul(), realloc() */
27 #include <string.h>             /* memcpy()             */
28 #include <unistd.h>             /* STDIN_FILENO,exec    */
29 #include <errno.h>              /* errno                */
30
31 #include <sys/time.h>
32
33 #include <net/ethernet.h>
34 #include <netinet/in.h>
35
36 #include <stdarg.h>
37 #include <ctype.h>
38 #include <syslog.h>
39 #include <limits.h>
40 #include <paths.h>
41
42 #include "pppoe.h"
43
44 static char pidfilename[PATH_MAX];      /* name of pid file */
45
46 /*
47 static int detached = 0;
48    log_to_fd = -1;
49  */
50
51 static void vslp_printer (void *, char *,...);
52 static void format_packet (struct pppoe_packet *, int, void (*)(void *, char *,...), void *);
53 static void format_tag (struct pppoe_tag *, void (*)(void *, char *,...), void *);
54 struct buffer_poe_info {
55   char *ptr;
56   int len;
57 };
58
59 void poe_die (int status);
60
61
62 /*
63  * vpoe_slprintf - like vsprintf, except we
64  * also specify the length of the output buffer, and we handle
65  * %r (recursive format), %m (poe_error message), %v (visible string),
66  * %q (quoted string), %t (current time) and %E (Ether address) formats.
67  * Doesn't do floating-point formats.
68  * Returns the number of chars put into buf.
69  */
70 #define OUTCHAR(c)      (buflen > 0? (--buflen, *buf++ = (c)): 0)
71
72 int
73 vpoe_slprintf (char *buf, int buflen, char *fmt, va_list args)
74 {
75   int c, i, n;
76   int width, prec, fillch;
77   int base, len, neg, quoted;
78   unsigned long val = 0;
79   char *str, *f, *buf0;
80   unsigned char *p;
81   char num[32];
82   time_t t;
83   static char hexchars[] = "0123456789abcdef";
84   struct buffer_poe_info bufpoe_info;
85
86   buf0 = buf;
87   --buflen;
88   while (buflen > 0) {
89     for (f = fmt; *f != '%' && *f != 0; ++f);
90     if (f > fmt) {
91       len = f - fmt;
92       if (len > buflen)
93         len = buflen;
94       memcpy (buf, fmt, len);
95       buf += len;
96       buflen -= len;
97       fmt = f;
98     }
99     if (*fmt == 0)
100       break;
101     c = *++fmt;
102     width = 0;
103     prec = -1;
104     fillch = ' ';
105     if (c == '0') {
106       fillch = '0';
107       c = *++fmt;
108     }
109     if (c == '*') {
110       width = va_arg (args, int);
111       c = *++fmt;
112     }
113     else {
114       while (isdigit (c)) {
115         width = width * 10 + c - '0';
116         c = *++fmt;
117       }
118     }
119     if (c == '.') {
120       c = *++fmt;
121       if (c == '*') {
122         prec = va_arg (args, int);
123         c = *++fmt;
124       }
125       else {
126         prec = 0;
127         while (isdigit (c)) {
128           prec = prec * 10 + c - '0';
129           c = *++fmt;
130         }
131       }
132     }
133     str = 0;
134     base = 0;
135     neg = 0;
136     ++fmt;
137     switch (c) {
138     case 'd':
139       i = va_arg (args, int);
140       if (i < 0) {
141         neg = 1;
142         val = -i;
143       }
144       else
145         val = i;
146       base = 10;
147       break;
148     case 'o':
149       val = va_arg (args, unsigned int);
150       base = 8;
151       break;
152     case 'x':
153     case 'X':
154       val = va_arg (args, unsigned int);
155       base = 16;
156       break;
157     case 'p':
158       val = (unsigned long) va_arg (args, void *);
159       base = 16;
160       neg = 2;
161       break;
162     case 's':
163       str = va_arg (args, char *);
164       break;
165     case 'c':
166       num[0] = va_arg (args, int);
167       num[1] = 0;
168       str = num;
169       break;
170     case 'm':
171       str = strerror (errno);
172       break;
173     case 'E':
174       p = va_arg (args, unsigned char *);
175       for (n = ETH_ALEN; n > 0; --n) {
176         c = *p++;
177         OUTCHAR (hexchars[(c >> 4) & 0xf]);
178         OUTCHAR (hexchars[c & 0xf]);
179         if (n > 1)
180           OUTCHAR (':');
181       }
182       continue;
183     case 'r':
184       f = va_arg (args, char *);
185 #ifndef __powerpc__
186       n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, va_list));
187 #else
188       /* On the powerpc, a va_list is an array of 1 structure */
189       n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, void *));
190 #endif
191       buf += n;
192       buflen -= n;
193       continue;
194     case 't':
195       time (&t);
196       str = ctime (&t);
197       str += 4;                 /* chop off the day name */
198       str[15] = 0;              /* chop off year and newline */
199       break;
200     case 'v':                   /* "visible" string */
201     case 'q':                   /* quoted string */
202       quoted = c == 'q';
203       p = va_arg (args, unsigned char *);
204       if (fillch == '0' && prec >= 0) {
205         n = prec;
206       }
207       else {
208         n = strlen ((char *) p);
209         if (prec >= 0 && n > prec)
210           n = prec;
211       }
212       while (n > 0 && buflen > 0) {
213         c = *p++;
214         --n;
215         if (!quoted && c >= 0x80) {
216           OUTCHAR ('M');
217           OUTCHAR ('-');
218           c -= 0x80;
219         }
220         if (quoted && (c == '"' || c == '\\'))
221           OUTCHAR ('\\');
222         if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
223           if (quoted) {
224             OUTCHAR ('\\');
225             switch (c) {
226             case '\t':
227               OUTCHAR ('t');
228               break;
229             case '\n':
230               OUTCHAR ('n');
231               break;
232             case '\b':
233               OUTCHAR ('b');
234               break;
235             case '\f':
236               OUTCHAR ('f');
237               break;
238             default:
239               OUTCHAR ('x');
240               OUTCHAR (hexchars[c >> 4]);
241               OUTCHAR (hexchars[c & 0xf]);
242             }
243           }
244           else {
245             if (c == '\t')
246               OUTCHAR (c);
247             else {
248               OUTCHAR ('^');
249               OUTCHAR (c ^ 0x40);
250             }
251           }
252         }
253         else
254           OUTCHAR (c);
255       }
256       continue;
257     case 'P':                   /* print PPPoE packet */
258       bufpoe_info.ptr = buf;
259       bufpoe_info.len = buflen + 1;
260       p = va_arg (args, unsigned char *);
261       n = va_arg (args, int);
262       format_packet ((struct pppoe_packet *) p, n, vslp_printer, &bufpoe_info);
263       buf = bufpoe_info.ptr;
264       buflen = bufpoe_info.len - 1;
265       continue;
266     case 'T':                   /* print PPPoE tag */
267       bufpoe_info.ptr = buf;
268       bufpoe_info.len = buflen + 1;
269       p = va_arg (args, unsigned char *);
270       format_tag ((struct pppoe_tag *) p, vslp_printer, &bufpoe_info);
271       buf = bufpoe_info.ptr;
272       buflen = bufpoe_info.len - 1;
273       continue;
274     case 'B':
275       p = va_arg (args, unsigned char *);
276       for (n = prec; n > 0; --n) {
277         c = *p++;
278         if (fillch == ' ')
279           OUTCHAR (' ');
280         OUTCHAR (hexchars[(c >> 4) & 0xf]);
281         OUTCHAR (hexchars[c & 0xf]);
282       }
283       continue;
284     default:
285       *buf++ = '%';
286       if (c != '%')
287         --fmt;                  /* so %z outputs %z etc. */
288       --buflen;
289       continue;
290     }
291     if (base != 0) {
292       str = num + sizeof (num);
293       *--str = 0;
294       while (str > num + neg) {
295         *--str = hexchars[val % base];
296         val = val / base;
297         if (--prec <= 0 && val == 0)
298           break;
299       }
300       switch (neg) {
301       case 1:
302         *--str = '-';
303         break;
304       case 2:
305         *--str = 'x';
306         *--str = '0';
307         break;
308       }
309       len = num + sizeof (num) - 1 - str;
310     }
311     else {
312       len = strlen (str);
313       if (prec >= 0 && len > prec)
314         len = prec;
315     }
316     if (width > 0) {
317       if (width > buflen)
318         width = buflen;
319       if ((n = width - len) > 0) {
320         buflen -= n;
321         for (; n > 0; --n)
322           *buf++ = fillch;
323       }
324     }
325     if (len > buflen)
326       len = buflen;
327     memcpy (buf, str, len);
328     buf += len;
329     buflen -= len;
330   }
331   *buf = 0;
332   return buf - buf0;
333 }
334
335 /*
336  * vslp_printer - used in processing a %P format
337  */
338 static void
339 vslp_printer (void *arg, char *fmt,...)
340 {
341   int n;
342   va_list pvar;
343   struct buffer_poe_info *bi;
344
345   va_start (pvar, fmt);
346
347   bi = (struct buffer_poe_info *) arg;
348   n = vpoe_slprintf (bi->ptr, bi->len, fmt, pvar);
349   va_end (pvar);
350
351   bi->ptr += n;
352   bi->len -= n;
353 }
354
355 /*
356  * format_packet - make a readable representation of a packet,
357  * calling `printer(arg, format, ...)' to output it.
358  */
359 static void
360 format_packet (struct pppoe_packet *p,
361                int len,
362                void (*printer) (void *, char *,...),
363                void *arg)
364 {
365   struct pppoe_tag *t;
366
367   printer (arg, "Ether addr: %E\n", p->addr.sll_addr);
368
369   switch ((unsigned) ntohs (p->addr.sll_protocol)) {
370   case ETH_P_PPPOE_DISC:
371     printer (arg, " (PPPOE Discovery)\n");
372     break;
373   case ETH_P_PPPOE_SESS:
374     printer (arg, " (PPPOE Session)\n");
375     break;
376   }
377
378   printer (arg, " PPPoE hdr: ver=0x%01x type=0x%01x code=0x%02x "
379            "sid=0x%04x length=0x%04x ", (unsigned) p->hdr->ver,
380            (unsigned) p->hdr->type, (unsigned) p->hdr->code, (unsigned) p->hdr->sid,
381            (unsigned) ntohs (p->hdr->length));
382
383   switch (p->hdr->code) {
384   case PADI_CODE:
385     printer (arg, "(PADI)\n");
386     break;
387   case PADO_CODE:
388     printer (arg, "(PADO)\n");
389     break;
390   case PADR_CODE:
391     printer (arg, "(PADR)\n");
392     break;
393   case PADS_CODE:
394     printer (arg, "(PADS)\n");
395     break;
396   case PADT_CODE:
397     printer (arg, "(PADT)\n");
398     break;
399   default:
400     printer (arg, "(Unknown)\n");
401   }
402
403 #if 0
404   if (ntohs (p->addr.sll_protocol) != ETH_P_PPPOE_DISC) {
405       len = ntohs (p->length);
406
407     if (len > 64)
408       printer (arg, " %.64B ...", (p + 1));
409     else
410       printer (arg, " %.*B", len, p + 1);
411
412     return;
413   }
414 #endif
415
416   for(t = (struct pppoe_tag *) (&p->hdr->tag);
417       (t < (struct pppoe_tag *) ((char *) (&p->hdr->tag) + ntohs (p->hdr->length))) &&
418           ntohs (t->tag_type) != PTT_EOL;
419       t = (struct pppoe_tag *) ((char *) (t + 1) + ntohs (t->tag_len))) {
420       format_tag (t, printer, arg);
421   }
422 }
423
424 /*
425  * format_tag - make a readable representation of a tag,
426  * calling `printer(arg, format, ...)' to output it.
427  */
428 static void
429 format_tag (struct pppoe_tag *t,
430                void (*printer) (void *, char *,...),
431                void *arg)
432 {
433     printer (arg, " PPPoE tag: type=%04x length=%04x ",
434              ntohs (t->tag_type), ntohs (t->tag_len));
435     switch ( t->tag_type ) {
436     case PTT_EOL:
437       printer (arg, "(End of list)");
438       break;
439     case PTT_SRV_NAME:
440       printer (arg, "(Service name)");
441       break;
442     case PTT_AC_NAME:
443       printer (arg, "(AC Name)");
444       break;
445     case PTT_HOST_UNIQ:
446       printer (arg, "(Host Uniq)");
447       break;
448     case PTT_AC_COOKIE:
449       printer (arg, "(AC Cookie)");
450       break;
451     case PTT_VENDOR:
452       printer (arg, "(Vendor Specific)");
453       break;
454     case PTT_RELAY_SID:
455       printer (arg, "(Relay Session ID)");
456       break;
457     case PTT_SRV_ERR:
458       printer (arg, "(Service Name Error)");
459       break;
460     case PTT_SYS_ERR:
461       printer (arg, "(AC System Error)");
462       break;
463     case PTT_GEN_ERR:
464       printer (arg, "(Generic Error)");
465       break;
466     default:
467       printer (arg, "(Unknown)");
468     }
469     if (ntohs (t->tag_len) > 0)
470       switch ( t->tag_type ) {
471       case PTT_SRV_NAME:
472       case PTT_AC_NAME:
473       case PTT_SRV_ERR:
474       case PTT_SYS_ERR:
475       case PTT_GEN_ERR: /* ascii data */
476         {
477           char *buf;
478           buf = malloc (ntohs (t->tag_len) + 1);
479           memset (buf, 0, ntohs (t->tag_len) + 1);
480           strncpy (buf, (char *) (t + 1), ntohs (t->tag_len));
481 //        buf[ntohs (t->tag_len)] = '\0';
482           printer (arg, " data (UTF-8): %s", buf);
483           free (buf);
484           break;
485         }
486
487       case PTT_HOST_UNIQ:
488       case PTT_AC_COOKIE:
489       case PTT_RELAY_SID:
490         printer (arg, " data (bin): %.*B", ntohs (t->tag_len), (char *) (t + 1));
491         break;
492
493       default:
494         printer (arg, " unrecognized data");
495       }
496 }
497
498 /*
499  * poe_logit - does the hard work for poe_fatal et al.
500  */
501 static void
502 poe_logit (struct session *ses,int level, char *fmt, va_list args)
503 {
504   int n;
505   char buf[256];
506
507   n = vpoe_slprintf (buf, sizeof (buf), fmt, args);
508   syslog (level, "%s", buf);
509   if (log_to_fd >= 0 && (level != LOG_DEBUG || ses->opt_debug)) {
510     if (buf[n - 1] != '\n')
511       buf[n++] = '\n';
512     if (write (log_to_fd, buf, n) != n)
513       log_to_fd = -1;
514   }
515 }
516
517 /*
518  * poe_fatal - log an poe_error message and poe_die horribly.
519  */
520 void
521 poe_fatal (struct session *ses, char *fmt,...)
522 {
523   va_list pvar;
524
525   va_start (pvar, fmt);
526
527   poe_logit (ses,LOG_ERR, fmt, pvar);
528   va_end (pvar);
529
530   poe_die(1);                   /* as promised */
531 }
532
533 /*
534  * poe_error - log an poe_error message.
535  */
536 void
537 poe_error (struct session *ses,char *fmt,...)
538 {
539   va_list pvar;
540
541   va_start (pvar, fmt);
542
543   poe_logit (ses,LOG_ERR, fmt, pvar);
544   va_end (pvar);
545 }
546
547 /*
548  * poe_warn - log a poe_warning message.
549  */
550 void
551 poe_warn (struct session *ses,char *fmt,...)
552 {
553   va_list pvar;
554
555   va_start (pvar, fmt);
556
557   poe_logit (ses,LOG_WARNING, fmt, pvar);
558   va_end (pvar);
559 }
560
561 #if 0
562 /*
563  * poe_notice - log a poe_notice-level message.
564  */
565 void
566 poe_notice (int log_to_fd ,char *fmt,...)
567 {
568   va_list pvar;
569
570   va_start (pvar, fmt);
571
572   poe_logit (log_to_fd,LOG_NOTICE, fmt, pvar);
573   va_end (pvar);
574 }
575
576 #endif
577 /*
578  * poe_info - log an poe_informational message.
579  */
580 void
581 poe_info (struct session *ses,char *fmt,...)
582 {
583   va_list pvar;
584
585   va_start (pvar, fmt);
586
587   poe_logit (ses,LOG_INFO, fmt, pvar);
588   va_end (pvar);
589 }
590
591 /*
592  * poe_dbglog - log a debug message.
593  */
594 void
595 poe_dbglog (struct session *ses ,char *fmt,...)
596 {
597   va_list pvar;
598
599   va_start (pvar, fmt);
600
601   poe_logit (ses,LOG_DEBUG, fmt, pvar);
602   va_end (pvar);
603 }
604
605 /*
606  * Create a file containing our process ID.
607  */
608 void
609 poe_create_pidfile (struct session *ses)
610 {
611   FILE *pidfile;
612
613   sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "pppoed");
614   if ((pidfile = fopen (pidfilename, "w")) != NULL) {
615     fprintf (pidfile, "%d\n", getpid ());
616     (void) fclose (pidfile);
617   }
618   else {
619     poe_error (ses,"Failed to create pid file %s: %m", pidfilename);
620     pidfilename[0] = 0;
621   }
622 }
623
624 /*
625  * detach - detach us from the controlling terminal.
626  */
627 void
628 poe_detach (struct session *ses)
629 {
630   if (ses->detached)
631     return;
632
633   if ((daemon (0, 0)) < 0) {
634     poe_error (ses,"Couldn't detach (daemon failed: %m)");
635 #if 0
636     poe_die (1);                        /* or just return? */
637 #endif
638   }
639   ses->detached = 1;
640   ses->log_to_fd = -1;
641   /* update pid files if they have been written already */
642   if (pidfilename[0])
643     poe_create_pidfile (ses);
644 }
645
646 /*
647  * cleanup - restore anything which needs to be restored before we exit
648  */
649 /* ARGSUSED */
650 static void
651 cleanup ()
652 {
653   if (pidfilename[0] != 0 && unlink (pidfilename) < 0 && errno != ENOENT)
654     syslog (LOG_INFO,"unable to delete pid file ");
655   pidfilename[0] = 0;
656 }
657
658 /*
659  * poe_die - clean up state and exit with the specified status.
660  */
661 void
662 poe_die (int status)
663 {
664   cleanup ();
665   syslog (LOG_INFO, "Exit.");
666   exit (status);
667 }