--- /dev/null
+
+/*
+ * utils.c - various utility functions used in pppoed.
+ *
+ * mostly stolen from ppp-2.3.10 by Marc Boucher <marc@mbsi.ca>
+ *
+ * Feb 18/2000 Made fully re-entrant (JHS)
+ *
+ * Copyright (c) 1999 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright poe_notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h> /* stdio */
+#include <stdlib.h> /* strtoul(), realloc() */
+#include <string.h> /* memcpy() */
+#include <unistd.h> /* STDIN_FILENO,exec */
+#include <errno.h> /* errno */
+
+#include <sys/time.h>
+
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include <stdarg.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <limits.h>
+#include <paths.h>
+
+#include "pppoe.h"
+
+static char pidfilename[PATH_MAX]; /* name of pid file */
+
+/*
+static int detached = 0;
+ log_to_fd = -1;
+ */
+
+static void vslp_printer (void *, char *,...);
+static void format_packet (struct pppoe_packet *, int, void (*)(void *, char *,...), void *);
+static void format_tag (struct pppoe_tag *, void (*)(void *, char *,...), void *);
+struct buffer_poe_info {
+ char *ptr;
+ int len;
+};
+
+void poe_die (int status);
+
+
+/*
+ * vpoe_slprintf - like vsprintf, except we
+ * also specify the length of the output buffer, and we handle
+ * %r (recursive format), %m (poe_error message), %v (visible string),
+ * %q (quoted string), %t (current time) and %E (Ether address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vpoe_slprintf (char *buf, int buflen, char *fmt, va_list args)
+{
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg, quoted;
+ unsigned long val = 0;
+ char *str, *f, *buf0;
+ unsigned char *p;
+ char num[32];
+ time_t t;
+ static char hexchars[] = "0123456789abcdef";
+ struct buffer_poe_info bufpoe_info;
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f);
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy (buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = 0;
+ prec = -1;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg (args, int);
+ c = *++fmt;
+ }
+ else {
+ while (isdigit (c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg (args, int);
+ c = *++fmt;
+ }
+ else {
+ prec = 0;
+ while (isdigit (c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'd':
+ i = va_arg (args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ }
+ else
+ val = i;
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg (args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ val = va_arg (args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg (args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg (args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg (args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ case 'm':
+ str = strerror (errno);
+ break;
+ case 'E':
+ p = va_arg (args, unsigned char *);
+ for (n = ETH_ALEN; n > 0; --n) {
+ c = *p++;
+ OUTCHAR (hexchars[(c >> 4) & 0xf]);
+ OUTCHAR (hexchars[c & 0xf]);
+ if (n > 1)
+ OUTCHAR (':');
+ }
+ continue;
+ case 'r':
+ f = va_arg (args, char *);
+#ifndef __powerpc__
+ n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, va_list));
+#else
+ /* On the powerpc, a va_list is an array of 1 structure */
+ n = vpoe_slprintf (buf, buflen + 1, f, va_arg (args, void *));
+#endif
+ buf += n;
+ buflen -= n;
+ continue;
+ case 't':
+ time (&t);
+ str = ctime (&t);
+ str += 4; /* chop off the day name */
+ str[15] = 0; /* chop off year and newline */
+ break;
+ case 'v': /* "visible" string */
+ case 'q': /* quoted string */
+ quoted = c == 'q';
+ p = va_arg (args, unsigned char *);
+ if (fillch == '0' && prec >= 0) {
+ n = prec;
+ }
+ else {
+ n = strlen ((char *) p);
+ if (prec >= 0 && n > prec)
+ n = prec;
+ }
+ while (n > 0 && buflen > 0) {
+ c = *p++;
+ --n;
+ if (!quoted && c >= 0x80) {
+ OUTCHAR ('M');
+ OUTCHAR ('-');
+ c -= 0x80;
+ }
+ if (quoted && (c == '"' || c == '\\'))
+ OUTCHAR ('\\');
+ if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+ if (quoted) {
+ OUTCHAR ('\\');
+ switch (c) {
+ case '\t':
+ OUTCHAR ('t');
+ break;
+ case '\n':
+ OUTCHAR ('n');
+ break;
+ case '\b':
+ OUTCHAR ('b');
+ break;
+ case '\f':
+ OUTCHAR ('f');
+ break;
+ default:
+ OUTCHAR ('x');
+ OUTCHAR (hexchars[c >> 4]);
+ OUTCHAR (hexchars[c & 0xf]);
+ }
+ }
+ else {
+ if (c == '\t')
+ OUTCHAR (c);
+ else {
+ OUTCHAR ('^');
+ OUTCHAR (c ^ 0x40);
+ }
+ }
+ }
+ else
+ OUTCHAR (c);
+ }
+ continue;
+ case 'P': /* print PPPoE packet */
+ bufpoe_info.ptr = buf;
+ bufpoe_info.len = buflen + 1;
+ p = va_arg (args, unsigned char *);
+ n = va_arg (args, int);
+ format_packet ((struct pppoe_packet *) p, n, vslp_printer, &bufpoe_info);
+ buf = bufpoe_info.ptr;
+ buflen = bufpoe_info.len - 1;
+ continue;
+ case 'T': /* print PPPoE tag */
+ bufpoe_info.ptr = buf;
+ bufpoe_info.len = buflen + 1;
+ p = va_arg (args, unsigned char *);
+ format_tag ((struct pppoe_tag *) p, vslp_printer, &bufpoe_info);
+ buf = bufpoe_info.ptr;
+ buflen = bufpoe_info.len - 1;
+ continue;
+ case 'B':
+ p = va_arg (args, unsigned char *);
+ for (n = prec; n > 0; --n) {
+ c = *p++;
+ if (fillch == ' ')
+ OUTCHAR (' ');
+ OUTCHAR (hexchars[(c >> 4) & 0xf]);
+ OUTCHAR (hexchars[c & 0xf]);
+ }
+ continue;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof (num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ }
+ len = num + sizeof (num) - 1 - str;
+ }
+ else {
+ len = strlen (str);
+ if (prec >= 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy (buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
+
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void
+vslp_printer (void *arg, char *fmt,...)
+{
+ int n;
+ va_list pvar;
+ struct buffer_poe_info *bi;
+
+ va_start (pvar, fmt);
+
+ bi = (struct buffer_poe_info *) arg;
+ n = vpoe_slprintf (bi->ptr, bi->len, fmt, pvar);
+ va_end (pvar);
+
+ bi->ptr += n;
+ bi->len -= n;
+}
+
+/*
+ * format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_packet (struct pppoe_packet *p,
+ int len,
+ void (*printer) (void *, char *,...),
+ void *arg)
+{
+ struct pppoe_tag *t;
+
+ printer (arg, "Ether addr: %E\n", p->addr.sll_addr);
+
+ switch ((unsigned) ntohs (p->addr.sll_protocol)) {
+ case ETH_P_PPPOE_DISC:
+ printer (arg, " (PPPOE Discovery)\n");
+ break;
+ case ETH_P_PPPOE_SESS:
+ printer (arg, " (PPPOE Session)\n");
+ break;
+ }
+
+ printer (arg, " PPPoE hdr: ver=0x%01x type=0x%01x code=0x%02x "
+ "sid=0x%04x length=0x%04x ", (unsigned) p->hdr->ver,
+ (unsigned) p->hdr->type, (unsigned) p->hdr->code, (unsigned) p->hdr->sid,
+ (unsigned) ntohs (p->hdr->length));
+
+ switch (p->hdr->code) {
+ case PADI_CODE:
+ printer (arg, "(PADI)\n");
+ break;
+ case PADO_CODE:
+ printer (arg, "(PADO)\n");
+ break;
+ case PADR_CODE:
+ printer (arg, "(PADR)\n");
+ break;
+ case PADS_CODE:
+ printer (arg, "(PADS)\n");
+ break;
+ case PADT_CODE:
+ printer (arg, "(PADT)\n");
+ break;
+ default:
+ printer (arg, "(Unknown)\n");
+ }
+
+#if 0
+ if (ntohs (p->addr.sll_protocol) != ETH_P_PPPOE_DISC) {
+ len = ntohs (p->length);
+
+ if (len > 64)
+ printer (arg, " %.64B ...", (p + 1));
+ else
+ printer (arg, " %.*B", len, p + 1);
+
+ return;
+ }
+#endif
+
+ for(t = (struct pppoe_tag *) (&p->hdr->tag);
+ (t < (struct pppoe_tag *) ((char *) (&p->hdr->tag) + ntohs (p->hdr->length))) &&
+ ntohs (t->tag_type) != PTT_EOL;
+ t = (struct pppoe_tag *) ((char *) (t + 1) + ntohs (t->tag_len))) {
+ format_tag (t, printer, arg);
+ }
+}
+
+/*
+ * format_tag - make a readable representation of a tag,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void
+format_tag (struct pppoe_tag *t,
+ void (*printer) (void *, char *,...),
+ void *arg)
+{
+ printer (arg, " PPPoE tag: type=%04x length=%04x ",
+ ntohs (t->tag_type), ntohs (t->tag_len));
+ switch ( t->tag_type ) {
+ case PTT_EOL:
+ printer (arg, "(End of list)");
+ break;
+ case PTT_SRV_NAME:
+ printer (arg, "(Service name)");
+ break;
+ case PTT_AC_NAME:
+ printer (arg, "(AC Name)");
+ break;
+ case PTT_HOST_UNIQ:
+ printer (arg, "(Host Uniq)");
+ break;
+ case PTT_AC_COOKIE:
+ printer (arg, "(AC Cookie)");
+ break;
+ case PTT_VENDOR:
+ printer (arg, "(Vendor Specific)");
+ break;
+ case PTT_RELAY_SID:
+ printer (arg, "(Relay Session ID)");
+ break;
+ case PTT_SRV_ERR:
+ printer (arg, "(Service Name Error)");
+ break;
+ case PTT_SYS_ERR:
+ printer (arg, "(AC System Error)");
+ break;
+ case PTT_GEN_ERR:
+ printer (arg, "(Generic Error)");
+ break;
+ default:
+ printer (arg, "(Unknown)");
+ }
+ if (ntohs (t->tag_len) > 0)
+ switch ( t->tag_type ) {
+ case PTT_SRV_NAME:
+ case PTT_AC_NAME:
+ case PTT_SRV_ERR:
+ case PTT_SYS_ERR:
+ case PTT_GEN_ERR: /* ascii data */
+ {
+ char *buf;
+ buf = malloc (ntohs (t->tag_len) + 1);
+ memset (buf, 0, ntohs (t->tag_len) + 1);
+ strncpy (buf, (char *) (t + 1), ntohs (t->tag_len));
+// buf[ntohs (t->tag_len)] = '\0';
+ printer (arg, " data (UTF-8): %s", buf);
+ free (buf);
+ break;
+ }
+
+ case PTT_HOST_UNIQ:
+ case PTT_AC_COOKIE:
+ case PTT_RELAY_SID:
+ printer (arg, " data (bin): %.*B", ntohs (t->tag_len), (char *) (t + 1));
+ break;
+
+ default:
+ printer (arg, " unrecognized data");
+ }
+}
+
+/*
+ * poe_logit - does the hard work for poe_fatal et al.
+ */
+static void
+poe_logit (struct session *ses,int level, char *fmt, va_list args)
+{
+ int n;
+ char buf[256];
+
+ n = vpoe_slprintf (buf, sizeof (buf), fmt, args);
+ syslog (level, "%s", buf);
+ if (log_to_fd >= 0 && (level != LOG_DEBUG || ses->opt_debug)) {
+ if (buf[n - 1] != '\n')
+ buf[n++] = '\n';
+ if (write (log_to_fd, buf, n) != n)
+ log_to_fd = -1;
+ }
+}
+
+/*
+ * poe_fatal - log an poe_error message and poe_die horribly.
+ */
+void
+poe_fatal (struct session *ses, char *fmt,...)
+{
+ va_list pvar;
+
+ va_start (pvar, fmt);
+
+ poe_logit (ses,LOG_ERR, fmt, pvar);
+ va_end (pvar);
+
+ poe_die(1); /* as promised */
+}
+
+/*
+ * poe_error - log an poe_error message.
+ */
+void
+poe_error (struct session *ses,char *fmt,...)
+{
+ va_list pvar;
+
+ va_start (pvar, fmt);
+
+ poe_logit (ses,LOG_ERR, fmt, pvar);
+ va_end (pvar);
+}
+
+/*
+ * poe_warn - log a poe_warning message.
+ */
+void
+poe_warn (struct session *ses,char *fmt,...)
+{
+ va_list pvar;
+
+ va_start (pvar, fmt);
+
+ poe_logit (ses,LOG_WARNING, fmt, pvar);
+ va_end (pvar);
+}
+
+#if 0
+/*
+ * poe_notice - log a poe_notice-level message.
+ */
+void
+poe_notice (int log_to_fd ,char *fmt,...)
+{
+ va_list pvar;
+
+ va_start (pvar, fmt);
+
+ poe_logit (log_to_fd,LOG_NOTICE, fmt, pvar);
+ va_end (pvar);
+}
+
+#endif
+/*
+ * poe_info - log an poe_informational message.
+ */
+void
+poe_info (struct session *ses,char *fmt,...)
+{
+ va_list pvar;
+
+ va_start (pvar, fmt);
+
+ poe_logit (ses,LOG_INFO, fmt, pvar);
+ va_end (pvar);
+}
+
+/*
+ * poe_dbglog - log a debug message.
+ */
+void
+poe_dbglog (struct session *ses ,char *fmt,...)
+{
+ va_list pvar;
+
+ va_start (pvar, fmt);
+
+ poe_logit (ses,LOG_DEBUG, fmt, pvar);
+ va_end (pvar);
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+void
+poe_create_pidfile (struct session *ses)
+{
+ FILE *pidfile;
+
+ sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "pppoed");
+ if ((pidfile = fopen (pidfilename, "w")) != NULL) {
+ fprintf (pidfile, "%d\n", getpid ());
+ (void) fclose (pidfile);
+ }
+ else {
+ poe_error (ses,"Failed to create pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ }
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+poe_detach (struct session *ses)
+{
+ if (ses->detached)
+ return;
+
+ if ((daemon (0, 0)) < 0) {
+ poe_error (ses,"Couldn't detach (daemon failed: %m)");
+#if 0
+ poe_die (1); /* or just return? */
+#endif
+ }
+ ses->detached = 1;
+ ses->log_to_fd = -1;
+ /* update pid files if they have been written already */
+ if (pidfilename[0])
+ poe_create_pidfile (ses);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup ()
+{
+ if (pidfilename[0] != 0 && unlink (pidfilename) < 0 && errno != ENOENT)
+ syslog (LOG_INFO,"unable to delete pid file ");
+ pidfilename[0] = 0;
+}
+
+/*
+ * poe_die - clean up state and exit with the specified status.
+ */
+void
+poe_die (int status)
+{
+ cleanup ();
+ syslog (LOG_INFO, "Exit.");
+ exit (status);
+}