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 44static char pidfilename[PATH_MAX]; /* name of pid file */ 45 46/* 47static int detached = 0; 48 log_to_fd = -1; 49 */ 50 51static void vslp_printer (void *, char *,...); 52static void format_packet (struct pppoe_packet *, int, void (*)(void *, char *,...), void *); 53static void format_tag (struct pppoe_tag *, void (*)(void *, char *,...), void *); 54struct buffer_poe_info { 55 char *ptr; 56 int len; 57}; 58 59void 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 72int 73vpoe_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 */ 338static void 339vslp_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 */ 359static void 360format_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 404 for(t = (struct pppoe_tag *) (&p->hdr->tag); 405 (t < (struct pppoe_tag *) ((char *) (&p->hdr->tag) + ntohs (p->hdr->length))) && 406 ntohs (t->tag_type) != PTT_EOL; 407 t = (struct pppoe_tag *) ((char *) (t + 1) + ntohs (t->tag_len))) { 408 format_tag (t, printer, arg); 409 } 410} 411 412/* 413 * format_tag - make a readable representation of a tag, 414 * calling `printer(arg, format, ...)' to output it. 415 */ 416static void 417format_tag (struct pppoe_tag *t, 418 void (*printer) (void *, char *,...), 419 void *arg) 420{ 421 printer (arg, " PPPoE tag: type=%04x length=%04x ", 422 ntohs (t->tag_type), ntohs (t->tag_len)); 423 switch ( t->tag_type ) { 424 case PTT_EOL: 425 printer (arg, "(End of list)"); 426 break; 427 case PTT_SRV_NAME: 428 printer (arg, "(Service name)"); 429 break; 430 case PTT_AC_NAME: 431 printer (arg, "(AC Name)"); 432 break; 433 case PTT_HOST_UNIQ: 434 printer (arg, "(Host Uniq)"); 435 break; 436 case PTT_AC_COOKIE: 437 printer (arg, "(AC Cookie)"); 438 break; 439 case PTT_VENDOR: 440 printer (arg, "(Vendor Specific)"); 441 break; 442 case PTT_RELAY_SID: 443 printer (arg, "(Relay Session ID)"); 444 break; 445 case PTT_SRV_ERR: 446 printer (arg, "(Service Name Error)"); 447 break; 448 case PTT_SYS_ERR: 449 printer (arg, "(AC System Error)"); 450 break; 451 case PTT_GEN_ERR: 452 printer (arg, "(Generic Error)"); 453 break; 454 default: 455 printer (arg, "(Unknown)"); 456 } 457 if (ntohs (t->tag_len) > 0) 458 switch ( t->tag_type ) { 459 case PTT_SRV_NAME: 460 case PTT_AC_NAME: 461 case PTT_SRV_ERR: 462 case PTT_SYS_ERR: 463 case PTT_GEN_ERR: /* ascii data */ 464 { 465 char *buf; 466 buf = malloc (ntohs (t->tag_len) + 1); 467 memset (buf, 0, ntohs (t->tag_len) + 1); 468 strncpy (buf, (char *) (t + 1), ntohs (t->tag_len)); 469// buf[ntohs (t->tag_len)] = '\0'; 470 printer (arg, " data (UTF-8): %s", buf); 471 free (buf); 472 break; 473 } 474 475 case PTT_HOST_UNIQ: 476 case PTT_AC_COOKIE: 477 case PTT_RELAY_SID: 478 printer (arg, " data (bin): %.*B", ntohs (t->tag_len), (char *) (t + 1)); 479 break; 480 481 default: 482 printer (arg, " unrecognized data"); 483 } 484} 485 486/* 487 * poe_logit - does the hard work for poe_fatal et al. 488 */ 489static void 490poe_logit (struct session *ses,int level, char *fmt, va_list args) 491{ 492 int n; 493 char buf[256]; 494 495 n = vpoe_slprintf (buf, sizeof (buf), fmt, args); 496 syslog (level, "%s", buf); 497 if (log_to_fd >= 0 && (level != LOG_DEBUG || ses->opt_debug)) { 498 if (buf[n - 1] != '\n') 499 buf[n++] = '\n'; 500 if (write (log_to_fd, buf, n) != n) 501 log_to_fd = -1; 502 } 503} 504 505/* 506 * poe_fatal - log an poe_error message and poe_die horribly. 507 */ 508void 509poe_fatal (struct session *ses, char *fmt,...) 510{ 511 va_list pvar; 512 513 va_start (pvar, fmt); 514 515 poe_logit (ses,LOG_ERR, fmt, pvar); 516 va_end (pvar); 517 518 poe_die(1); /* as promised */ 519} 520 521/* 522 * poe_error - log an poe_error message. 523 */ 524void 525poe_error (struct session *ses,char *fmt,...) 526{ 527 va_list pvar; 528 529 va_start (pvar, fmt); 530 531 poe_logit (ses,LOG_ERR, fmt, pvar); 532 va_end (pvar); 533} 534 535/* 536 * poe_warn - log a poe_warning message. 537 */ 538void 539poe_warn (struct session *ses,char *fmt,...) 540{ 541 va_list pvar; 542 543 va_start (pvar, fmt); 544 545 poe_logit (ses,LOG_WARNING, fmt, pvar); 546 va_end (pvar); 547} 548 549/* 550 * poe_info - log an poe_informational message. 551 */ 552void 553poe_info (struct session *ses,char *fmt,...) 554{ 555 va_list pvar; 556 557 va_start (pvar, fmt); 558 559 poe_logit (ses,LOG_INFO, fmt, pvar); 560 va_end (pvar); 561} 562 563/* 564 * poe_dbglog - log a debug message. 565 */ 566void 567poe_dbglog (struct session *ses ,char *fmt,...) 568{ 569 va_list pvar; 570 571 va_start (pvar, fmt); 572 573 poe_logit (ses,LOG_DEBUG, fmt, pvar); 574 va_end (pvar); 575} 576 577/* 578 * Create a file containing our process ID. 579 */ 580void 581poe_create_pidfile (struct session *ses) 582{ 583 FILE *pidfile; 584 585 sprintf (pidfilename, "%s%s.pid", _PATH_VARRUN, "pppoed"); 586 if ((pidfile = fopen (pidfilename, "w")) != NULL) { 587 fprintf (pidfile, "%d\n", getpid ()); 588 (void) fclose (pidfile); 589 } 590 else { 591 poe_error (ses,"Failed to create pid file %s: %m", pidfilename); 592 pidfilename[0] = 0; 593 } 594} 595 596/* 597 * detach - detach us from the controlling terminal. 598 */ 599void 600poe_detach (struct session *ses) 601{ 602 if (ses->detached) 603 return; 604 605 if ((daemon (0, 0)) < 0) { 606 poe_error (ses,"Couldn't detach (daemon failed: %m)"); 607 } 608 ses->detached = 1; 609 ses->log_to_fd = -1; 610 /* update pid files if they have been written already */ 611 if (pidfilename[0]) 612 poe_create_pidfile (ses); 613} 614 615/* 616 * cleanup - restore anything which needs to be restored before we exit 617 */ 618/* ARGSUSED */ 619static void 620cleanup () 621{ 622 if (pidfilename[0] != 0 && unlink (pidfilename) < 0 && errno != ENOENT) 623 syslog (LOG_INFO,"unable to delete pid file "); 624 pidfilename[0] = 0; 625} 626 627/* 628 * poe_die - clean up state and exit with the specified status. 629 */ 630void 631poe_die (int status) 632{ 633 cleanup (); 634 syslog (LOG_INFO, "Exit."); 635 exit (status); 636} 637