/*************************************************/ /* Center for Information Technology Integration */ /* The University of Michigan */ /* Ann Arbor */ /* */ /* Dedicated to the public domain. */ /* Send questions to info@citi.umich.edu */ /* */ /* BOOTP is documented in RFC 951 and RFC 1048 */ /*************************************************/ #include #include #include #include #include #ifdef ANSIPROTO #include #endif #include "global.h" #include "config.h" #include "cmdparse.h" #include "bootpd.h" #include "netuser.h" #include "iface.h" #include "udp.h" #include "arp.h" #define BP_DEFAULT_TAB "bootptab" #define BP_DEFAULT_LOG "bootplog" #define BP_DEFAULT_DIR "bpfiles" #define BP_DEFAULT_FILE "boot" static char *bootptab = BP_DEFAULT_TAB; static FILE *bootfp; /* bootptab fp */ static long modtime; /* last modification time of bootptab */ static char bootplog[64] = BP_DEFAULT_LOG; static int LogInFile = 0; /* Should bp_log log in a file? */ static int LogOnScreen = 0; /* Should bp_log log on screen? */ static char *line; /* line buffer for reading bootptab */ static int linenum; /* current ilne number in bootptab */ extern int Nhosts; /* number of hosts in host structure */ extern struct host hosts[MHOSTS]; extern char homedir[64]; /* bootfile homedirectory */ extern char defaultboot[64]; /* default file to boot */ extern int32 bp_DefaultDomainNS[BP_MAXDNS]; /* default domain name server */ extern int Nhosts; extern struct udp_cb *Bootpd_cb; static int bp_Homedir __ARGS((int argc,char *argv[],void *p)); static int bp_DefaultFile __ARGS((int argc,char *argv[],void *p)); static int bp_DynamicRange __ARGS((int argc,char *argv[],void *p)); static int bp_donothing __ARGS((int argc,char *argv[],void *p)); static int bp_Host __ARGS((int argc,char *argv[],void *p)); static int bp_rmHost __ARGS((int argc,char *argv[],void *p)); static int bp_DomainNS __ARGS((int argc,char *argv[],void *p)); static int bp_Start __ARGS((int argc,char *argv[],void *p)); static int bp_Stop __ARGS((int argc,char *argv[],void *p)); static int bp_logFile __ARGS((int argc,char *argv[],void *p)); static int bp_logScreen __ARGS((int argc,char *argv[],void *p)); void bootpd __ARGS((struct iface *iface, struct udp_cb *sock, int cnt)); static struct cmds BootpdCmds[] = { "", bp_donothing, 0, 0, NULLCHAR, "start", bp_Start, 0, 0, NULLCHAR, "stop", bp_Stop, 0, 0, NULLCHAR, "dns", bp_DomainNS, 0, 0, NULLCHAR, "dynip", bp_DynamicRange, 0, 0, NULLCHAR, "host", bp_Host, 0, 0, NULLCHAR, "rmhost", bp_rmHost, 0, 0, NULLCHAR, "homedir", bp_Homedir, 0, 0, NULLCHAR, "defaultfile", bp_DefaultFile, 0, 0, NULLCHAR, "logfile", bp_logFile, 0, 0, NULLCHAR, "logscreen", bp_logScreen, 0, 0, NULLCHAR, NULLCHAR, NULLFP, 0, 0, NULLCHAR }; int bootpdcmd (argc, argv, p) int argc; char *argv[]; void *p; { return subcmd (BootpdCmds, argc, argv, p); } /* Start up bootp service */ static int bp_Start (argc,argv,p) int argc; char *argv[]; void *p; { struct socket lsock; time_t tloc; char *usage = "bootpd start\n"; if (argc != 1) { tprintf (usage); return (-1); } time(&tloc); bp_log ("\n\n####BOOTP server starting at %s\n", ctime(&tloc)); lsock.address = INADDR_ANY; lsock.port = IPPORT_BOOTPS; /* This way is better than recvfrom because it passes the iface in bootpd call */ /* Listen doesn't work for datagrams. */ if (Bootpd_cb == NULLUDP) { if ((Bootpd_cb = open_udp(&lsock, bootpd)) == NULLUDP) { tprintf ("bootpd: can't open_udp\n"); return (-1); } } /* * Read the bootptab file once immediately upon startup. */ da_init(); readtab(); return (0); } /* Stop bootp service */ static int bp_Stop (argc, argv, p) int argc; char *argv[]; void *p; { time_t now; char *usage = "bootpd stop\n"; if (argc != 1) { tprintf (usage); return -1; } time (&now); Nhosts = 0; da_shut(); readtab_shut(); del_udp (Bootpd_cb); Bootpd_cb = NULLUDP; bp_log ("Bootpd shutdown %s", ctime (&now)); return (0); }; static int bp_logFile (argc, argv, p) int argc; char *argv[]; void *p; { int i; time_t now; char *usage = "bootpd logfile [ | default] [on | off] \n"; time (&now); if (argc == 1) { if (LogInFile) tprintf ("Bootpd logging to file '%s' turned on.\n", bootplog); else tprintf ("Bootpd logging to file '%s' turned off.\n", bootplog); } else { for (i = 1; i < argc; i++) { if (strcmp ("?", argv[i]) == 0) tprintf (usage); else if (strcmp ("off", argv[i]) == 0) { bp_log ("Stopping file logging at %s", ctime(&now)); LogInFile = 0; } else if (strcmp ("on", argv[i]) == 0) { LogInFile = 1; bp_log ("Starting file logging at %s", ctime(&now)); } else if (strcmp ("default", argv[i]) == 0) { strcpy (bootplog, BP_DEFAULT_LOG); bp_log ("File for logging set to %s\n", bootplog); } else { strcpy (bootplog, argv[1]); bp_log ("File for logging set to %s\n", bootplog); } } } return 0; } static int bp_logScreen (argc, argv, p) int argc; char *argv[]; void *p; { char *usage = "bootpd logscreen [on | off]\n"; if (argc == 1) if (LogOnScreen) tprintf ("Bootpd logging on screen turned on.\n"); else tprintf ("Bootpd logging on screen turned off.\n"); else if (argc == 2) { if (strcmp ("on", argv[1]) == 0) LogOnScreen = 1; else if (strcmp ("off", argv[1]) == 0) LogOnScreen = 0; else tprintf (usage); } else tprintf (usage); return 0; } static int bp_DomainNS (argc, argv, p) int argc; char *argv[]; void *p; { int a0, a1, a2, a3; int i; char *usage = "bootpd dns [...]\n"; if (argc == 1) { tprintf ("Bootp domain name servers: "); for (i=0; (i < BP_MAXDNS) && (bp_DefaultDomainNS[i] != 0); i++) tprintf (" %s", inet_ntoa (bp_DefaultDomainNS[i])); tprintf ("\n"); return (0); } if (argc > 1) { if ((argc == 2) && (strcmp ("?", argv[1]) == 0)) { tprintf (usage); return 0; } /* A list of name servers has been given */ /* reset the domain name server list */ for (i= 0; i < BP_MAXDNS; i++) bp_DefaultDomainNS[i] = 0; /* get ip address */ for (i = 1; (i < argc) && (i < BP_MAXDNS); i++) { if (4 != sscanf (argv[i], "%d.%d.%d.%d", &a0, &a1, &a2, &a3)) { tprintf("bad internet address: %s\n", argv[1], linenum); return -1; } bp_DefaultDomainNS[i-1] = aton(argv[i]); } } /* record for the loggers sake */ bp_log ("Bootp domain name servers: "); for (i=0; (i < BP_MAXDNS) && (bp_DefaultDomainNS[i] != 0); i++) bp_log (" %s", inet_ntoa (bp_DefaultDomainNS[i])); bp_log ("\n"); return 0; } static int bp_rmHost (argc, argv, p) int argc; char *argv[]; void *p; { int i; struct host *hp = NULLHOST; struct host *cp = NULLHOST; char *usage = "bootpd rmhost \n"; if (argc == 2) { /* Find the host record */ for (i=0; i < Nhosts; i++) { if (strcmp (hosts[i].name, argv[1]) == 0) { hp = &(hosts[i]); break; } } /* Return if not found */ if (hp == NULLHOST) { tprintf ("Host %s not in host tables.\n", argv[1]); return -1; } bp_log ("Host %s removed from host table\n", hp->name); cp = &(hosts [Nhosts - 1]); if (hp < cp) memcpy ((char *) hp, (char *) cp, sizeof (struct host)); Nhosts--; return 0; } else tprintf (usage); return 0; } /* * Printout the hosts table. */ static void dumphosts() { int i; struct host *hp; struct arp_type *at; tprintf ("\n\nStatus of host table\n"); if (Nhosts == 0) { tprintf (" No hosts in host table\n"); return; } for (i = 0; i <= Nhosts-1; i++) { hp = &hosts[i]; at = &Arp_type[hp->htype]; tprintf ("%s %s %s %s '%s'\n", hp->name, ArpNames[hp->htype], (*at->format)(bp_ascii, hp->haddr), inet_ntoa ((int32)hp->iaddr.s_addr), hp->bootfile); } } static int bp_Host (argc, argv, p) int argc; char *argv[]; void *p; { struct host *hp; int a0, a1, a2, a3; struct arp_type *at; char *usage = "bootpd host [ [boot file]]\n"; switch (argc) { case 1: dumphosts(); break; case 5: case 6: hp = &hosts[Nhosts]; /* get host name */ strncpy (hp->name, argv[1], sizeof (hp->name)); /* get hardware type */ /* This code borrowed from Phil Karn's arpcmd.c */ /* This is a kludge. It really ought to be table driven */ switch(tolower(argv[2][0])){ case 'n': /* Net/Rom pseudo-type */ hp->htype = ARP_NETROM; break; case 'e': /* "ether" */ hp->htype = ARP_ETHER; break; case 'a': /* "ax25" */ hp->htype = ARP_AX25; break; case 'm': /* "mac appletalk" */ hp->htype = ARP_APPLETALK; break; default: tprintf("unknown hardware type \"%s\"\n",argv[2]); return -1; } at = &Arp_type[hp->htype]; if(at->scan == NULLFP){ return 1; } /* Destination address */ (*at->scan)(hp->haddr,argv[3]); /* get ip address */ if (4 != sscanf (argv[4], "%d.%d.%d.%d", &a0, &a1, &a2, &a3)) { tprintf("bad internet address: %s\n", argv[1], linenum); return (0); } hp->iaddr.s_addr = aton(argv[4]); /* get the bootpfile */ if (argc == 6) strncpy (hp->bootfile, argv[5], sizeof (hp->bootfile)); else hp->bootfile[0] = 0; bp_log ("Host added: %s %s %s %s '%s'\n", hp->name, ArpNames[hp->htype], (*at->format)(bp_ascii, hp->haddr), inet_ntoa ((int32)hp->iaddr.s_addr), hp->bootfile); Nhosts++; break; default: tprintf (usage); break; } return 0; } static int bp_Homedir (argc, argv, p) int argc; char *argv[]; void *p; { char *usage = "bootpd homedir [ | default]\n"; if (argc == 1) tprintf ("Bootp home directory: '%s'\n", homedir); else if (argc == 2) { if (strcmp (argv[1], "?") == 0) tprintf (usage); else if (strcmp (argv[1], "default") == 0) { strcpy (homedir, BP_DEFAULT_DIR); bp_log ("Bootp home directory set to: '%s'\n", homedir); } else { strcpy (homedir, argv[1]); bp_log ("Bootp home directory set to: '%s'\n", homedir); } } else tprintf (usage); return (0); }; static int bp_DefaultFile (argc, argv, p) int argc; char *argv[]; void *p; { char *usage = "bootpd defaultfile [ | default]\n"; if (argc == 1) tprintf ("Bootp default boot file: '%s'\n", defaultboot); else if (argc == 2) { if (strcmp (argv[1], "?") == 0) tprintf (usage); else if (strcmp (argv[1], "default") == 0) strcpy (defaultboot, BP_DEFAULT_FILE); else { strcpy (defaultboot, argv[1]); bp_log ("Bootp default boot file set to: '%s'\n", defaultboot); } } else tprintf (usage); return (0); }; static int bp_DynamicRange (argc, argv, p) int argc; char *argv[]; void *p; { int i0, i1, i2, i3; int32 start, end; struct iface *iface; char *usage = "bootpd dynip [ | | off]\n"; if (argc == 1) { da_status (NULLIF); return 0; } if ((argc == 2) && (strcmp ("?", argv[1]) == 0)) { tprintf (usage); return 0; } /* get the interface */ iface = if_lookup (argv[1]); if (iface == NULLIF) { tprintf ("network '%s' not found\n", argv[1]); return (-1); } if (argc == 2) { da_status (iface); return 0; } if (argc == 3) { if (strcmp ("off", argv[2]) == 0) da_done_net (iface); else tprintf (usage); } else if (argc == 4) { /* get ip address */ /* check the ip address - isaddr isn't a ka9q function */ if ((4 != sscanf (argv[2], "%d.%d.%d.%d", &i0, &i1, &i2, &i3)) || (i0 > 255) || (i1 > 255) || (i2 > 255) || (i3 > 255) ) { tprintf("bad internet address: %s\n", argv[2], linenum); return (-1); } if ((4 != sscanf (argv[3], "%d.%d.%d.%d", &i0, &i1, &i2, &i3)) || (i0 > 255) || (i1 > 255) || (i2 > 255) || (i3 > 255) ) { tprintf("bad internet address: %s\n", argv[3], linenum); return (-1); } start = aton(argv[2]); end = aton(argv[3]); da_serve_net (iface, start, end); } else { tprintf (usage); return (0); } return (0); }; static int bp_donothing (argc, argv, p) int argc; char *argv[]; void *p; { return (0); } /* * Read bootptab database file. Avoid rereading the file if the * write date hasn't changed since the last time we read it. */ int readtab() { struct stat st; /* If the file hasn't been opened, open it. */ if (bootfp == 0) { if ((bootfp = fopen(bootptab, "r")) == NULLFILE) { bp_log("Can't open bootptab file: %s\n", bootptab); return (-1); } } /* Only reread if modified */ stat (bootptab, &st); if (st.st_mtime == modtime && st.st_nlink) { return (0); /* hasnt been modified or deleted yet */ } /* It's been changed, reread. */ if ((bootfp = fopen(bootptab, "r")) == NULLFILE) { bp_log("Can't open %s\n", bootptab); return (-1); } fstat(fileno(bootfp), &st); bp_log("(re)reading %s\n", bootptab); modtime = st.st_mtime; /* * read and parse each line in the file. */ line = mallocw(BUFSIZ); while (fgets(line, BUFSIZ, bootfp) != NULLCHAR) { linenum++; if ((line[0] == 0) || (line[0] == '#') || (line[0] == ' ')) continue; if (cmdparse (BootpdCmds, line, NULL) == -1) continue; } fclose(bootfp); free (line); return (0); } void readtab_shut() { modtime = 0; } /* * log an error message * */ #if defined(ANSIPROTO) void bp_log(char *fmt,...) { FILE *fp; va_list ap; if (LogOnScreen) { va_start(ap,fmt); vprintf(fmt, ap); va_end(ap); fflush (stdout); } if (LogInFile) { if ((fp = fopen(bootplog, "a+")) == NULL) { tprintf ("Cannot open bootplog.\n"); return; } va_start(ap,fmt); vfprintf(fp, fmt, ap); va_end(ap); fflush(fp); fclose(fp); } } #else void bp_log(fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10) char *fmt; char *argv1; char *argv2; char *argv3; char *argv4; char *argv5; char *argv6; char *argv7; char *argv8; char *argv9; char *argv10; { FILE *fp; if (LogOnScreen) { printf(fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10); fflush (stdout); } if (LogInFile) { if ((fp = fopen(bootplog, "a+")) == NULL) { tprintf ("Cannot open bootplog.\n"); return; } fprintf(fp, fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10); fflush (fp); fclose(fp); } } #endif