/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* bos720 src/bos/usr/sbin/bootpd/readfile.c 1.13.3.1 */ /* */ /* Licensed Materials - Property of IBM */ /* */ /* COPYRIGHT International Business Machines Corp. 1991,2011 */ /* All Rights Reserved */ /* */ /* US Government Users Restricted Rights - Use, duplication or */ /* disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /* */ /* IBM_PROLOG_END_TAG */ static char sccsid[] = "@(#)20 1.13.3.1 src/bos/usr/sbin/bootpd/readfile.c, cmdnet, bos720 7/14/11 19:41:55"; /** ** COMPONENT_NAME: (CMDNET) readfile.c ** ** FUNCTIONS: ** ** PRIVATE void adjust(s) ** PRIVATE void del_bindata(dataptr) ** PRIVATE void del_iplist(iplist) ** PRIVATE void del_string(stringptr) ** PRIVATE struct bindata *bindup(struct bindata *) ** PRIVATE struct in_addr_list *iplistdup(struct in_addr_list *) ** PRIVATE char *get_str(char **) ** PRIVATE void eat_whitespace(s) ** PRIVATE eval_symbol(symbol, hp) ** PRIVATE void fill_defaults(hp, src) ** PRIVATE void free_host(hostptr) ** PRIVATE struct in_addr_list *get_addresses(src) ** PRIVATE char *get_string(src, dest, length) ** PRIVATE u_long get_u_long(src) ** PRIVATE boolean goodname(hostname) ** PRIVATE boolean hwinscmp(host1, host2) ** PRIVATE int interp_byte(src, retbyte) ** PRIVATE void makelower(s) ** PRIVATE boolean nmcmp(name, hp) ** PRIVATE boolean nullcmp(host1, host2) ** PRIVATE int process_entry(host, src) ** PRIVATE void process_generic(src, dest, tagvalue) ** PRIVATE byte *prs_haddr(src, htype) ** PRIVATE prs_inetaddr(src, result) ** PRIVATE void read_entry(fp, buffer, bufsiz) ** int readtab() ** PRIVATE char *smalloc(nbytes) ** ** ** ORIGINS: 6,44,27 ** ** (C) COPYRIGHT International Business Machines Corp. 1991 ** All Rights Reserved ** Licensed Materials - Property of IBM ** US Government Users Restricted Rights - Use, duplication or ** disclosure restricted by GSA ADP Schedule Contract with IBM Corp. ** ** Permission to use, copy, modify, and distribute this program for any ** purpose and without fee is hereby granted, provided that this copyright, ** permission and disclaimer notice appear on all copies and supporting ** documentation; the name of IBM not be used in advertising or publicity ** pertaining to distribution of the program without specific prior ** permission; and notice be given in supporting documentation that copying ** and distribution is by permission of IBM. IBM makes no representations ** about the suitability of this software for any purpose. This program ** is provided "as is" without any express or implied warranties, including, ** without limitation, the implied warranties of merchantability and fitness ** for a particular purpose. ** ** US Government Users Restricted Rights - Use, duplication or ** disclosure restricted by GSA ADP Schedule Contract with IBM Corp. ** ** (C) COPYRIGHT Advanced Graphics Engineering 1990 ** All Rights Reserved ** ** ** bootpd configuration file reading code. ** ** The routines in this file deal with reading, interpreting, and storing ** the information found in the bootpd configuration file (usually ** /etc/bootptab). ** ** ** ** Copyright (c) 1988 by Carnegie Mellon. ** ** Permission to use, copy, modify, and distribute this program for any ** purpose and without fee is hereby granted, provided that this copyright ** and permission notice appear on all copies and supporting documentation, ** the name of Carnegie Mellon not be used in advertising or publicity ** pertaining to distribution of the program without specific prior ** permission, and notice be given in supporting documentation that copying ** and distribution is by permission of Carnegie Mellon and Stanford ** University. Carnegie Mellon makes no representations about the ** suitability of this software for any purpose. It is provided "as is" ** without express or implied warranty. **/ #include #include #include #include #include #include #include #include #include /* * makes no difference to readfile.c whether AIX31 or AIX32 */ #ifdef AIX32 #define AIX31 #endif #ifdef AIX31 #define _USE_NLS #define _USE_SYSLOG #define _USE_DEBUG #endif #ifdef AIXBUILD #include #endif #ifdef _USE_NLS #include #endif #ifdef _USE_SYSLOG #include #else #define LOG_INFO 6 #define LOG_ERR 3 #define LOG_NOTICE 5 #endif #include "bootp.h" #include "hash.h" #include "bootpd.h" #define SUCCESS 0 #define E_END_OF_ENTRY (-1) #define E_SYNTAX_ERROR (-2) #define E_UNKNOWN_SYMBOL (-3) #define E_BAD_IPADDR (-4) #define E_BAD_HADDR (-5) #define E_BAD_SMASK (-6) #define E_BAD_TIMEOFF (-7) #define E_BAD_VM_COOKIE (-8) #define E_BAD_HTYPE (-9) #define E_BAD_BOOTSIZE (-10) #define E_BAD_BOOT_SERVER (-11) #define SYM_NULL 0 #define SYM_BOOTFILE 1 #define SYM_COOKIE_SERVER 2 #define SYM_DOMAIN_SERVER 3 #define SYM_GATEWAY 4 #define SYM_HADDR 5 #define SYM_HOMEDIR 6 #define SYM_HTYPE 7 #define SYM_IMPRESS_SERVER 8 #define SYM_IPADDR 9 #define SYM_LOG_SERVER 10 #define SYM_LPR_SERVER 11 #define SYM_NAME_SERVER 12 #define SYM_RLP_SERVER 13 #define SYM_SUBNET_MASK 14 #define SYM_TIME_OFFSET 15 #define SYM_TIME_SERVER 16 #define SYM_VENDOR_MAGIC 17 #define SYM_SIMILAR_ENTRY 18 #define SYM_NAME_SWITCH 19 #define SYM_BOOTSIZE 20 #define SYM_BOOT_SERVER 22 #define SYM_BOOT_TYPE 23 #define SYM_DUMB_TERMINAL 24 #define SYM_DOMAIN 25 #define OP_ADDITION 1 /* Operations on tags */ #define OP_DELETION 2 #define OP_BOOLEAN 3 #define MAXINADDRS 16 /* Max size of an IP address list */ #define MAXBUFLEN 64 /* Max temp buffer space */ #define MAXENTRYLEN 1024 /* Max size of an entire entry */ /* * Structure used to map a configuration-file symbol (such as "ds") to a * unique integer. */ struct symbolmap { char *symbol; int symbolcode; }; struct htypename { char *name; byte htype; }; PRIVATE int nhosts; /* Current number of hosts */ PRIVATE long modtime = 0; /* Last modification time of bootptab */ /* * List of symbolic names used in the bootptab file. The order and actual * values of the symbol codes (SYM_. . .) are unimportant, but they must * all be unique. */ PRIVATE struct symbolmap symbol_list[] = { { "bf", SYM_BOOTFILE }, { "cs", SYM_COOKIE_SERVER }, { "ds", SYM_DOMAIN_SERVER }, { "gw", SYM_GATEWAY }, { "ha", SYM_HADDR }, { "hd", SYM_HOMEDIR }, { "ht", SYM_HTYPE }, { "im", SYM_IMPRESS_SERVER }, { "ip", SYM_IPADDR }, { "lg", SYM_LOG_SERVER }, { "lp", SYM_LPR_SERVER }, { "ns", SYM_NAME_SERVER }, { "rl", SYM_RLP_SERVER }, { "sa", SYM_BOOT_SERVER }, { "sm", SYM_SUBNET_MASK }, { "to", SYM_TIME_OFFSET }, { "ts", SYM_TIME_SERVER }, { "vm", SYM_VENDOR_MAGIC }, { "tc", SYM_SIMILAR_ENTRY }, { "hn", SYM_NAME_SWITCH }, { "bs", SYM_BOOTSIZE }, { "bt", SYM_BOOT_TYPE }, { "dt", SYM_DUMB_TERMINAL }, { "dn", SYM_DOMAIN } }; /* * List of symbolic names for hardware types. Name translates into * hardware type code listed with it. Names must begin with a letter * and must be all lowercase. This is searched linearly, so put * commonly-used entries near the beginning. */ PRIVATE struct htypename htnamemap[] = { { "ethernet", HTYPE_ETHERNET }, { "ethernet3", HTYPE_EXP_ETHERNET }, { "ether", HTYPE_ETHERNET }, { "ether3", HTYPE_EXP_ETHERNET }, { "ieee802", HTYPE_IEEE8023 }, /* alt 8023 */ { "tr", HTYPE_IEEE802 }, { "token-ring", HTYPE_IEEE802 }, { "fddi", HTYPE_FDDI }, { "atm", HTYPE_ATM }, { "pronet", HTYPE_PRONET }, { "chaos", HTYPE_CHAOS }, { "arcnet", HTYPE_ARCNET }, { "ax.25", HTYPE_AX25 }, { "hyperchannel", HTYPE_HYPERCHANNEL }, { "lanstar", HTYPE_LANSTAR }, { "autonet", HTYPE_AUTONET }, { "localtalk", HTYPE_LOCALTALK }, { "localnet", HTYPE_LOCALNET }, { "ultra_link", HTYPE_ULTRA_LINK }, { "smds", HTYPE_SMDS }, { "frame_relay", HTYPE_FRAME_RELAY }, { "hfi", HTYPE_HFI } }; /* * Externals and forward declarations. */ extern char *malloc(); extern boolean iplookcmp(); PRIVATE char *smalloc(); PRIVATE void fill_defaults(); PRIVATE void del_string(); PRIVATE void del_bindata(); PRIVATE void del_iplist(); PRIVATE void free_host(); PRIVATE u_long get_u_long(); PRIVATE void process_generic(); PRIVATE char *get_string(); PRIVATE void read_entry(); PRIVATE boolean nullcmp(); PRIVATE boolean nmcmp(); PRIVATE boolean hwinscmp(); PRIVATE void adjust(); PRIVATE void eat_whitespace(); PRIVATE void makelower(); PRIVATE struct in_addr_list *get_addresses(); PRIVATE byte *prs_haddr(); PRIVATE struct in_addr_list *iplistdup(); PRIVATE struct bindata *bindup(); PRIVATE char *get_str(); PRIVATE char *strdup(); /* * 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 host *hp; FILE *fp; struct stat st; unsigned hashcode, buflen; static char buffer[MAXENTRYLEN]; #ifdef _USE_DEBUG char timestr[26]; #endif /* * Check the last modification time. */ if (stat(bootptab, &st) < 0) { report(LOG_ERR, "stat on \"%s\": %s\n", bootptab, get_errmsg()); return -1; } #ifdef _USE_DEBUG if (debug > 2) { strcpy(timestr, ctime(&(st.st_mtime))); report(LOG_INFO, "bootptab mtime is %s", timestr); } #endif if (st.st_mtime <= modtime && st.st_nlink) { /* * hasn't been modified or deleted yet. */ return 0; } if (debug) { report(LOG_INFO, "reading %s\"%s\"\n", (modtime != 0L) ? "new " : "", bootptab); } /* * Open bootptab file. */ if ((fp = fopen(bootptab, "r")) == NULL) { report(LOG_ERR, "error opening \"%s\": %s\n", bootptab, get_errmsg()); return -1; } /* * Record file modification time. */ if (fstat(fileno(fp), &st) < 0) { report(LOG_ERR, "fstat: %s\n", get_errmsg()); fclose(fp); return -1; } modtime = st.st_mtime; /* * Entirely erase all hash tables. */ hash_Reset(hwhashtable, free_host); hash_Reset(iphashtable, free_host); hash_Reset(nmhashtable, free_host); nhosts = 0; while (TRUE) { buflen = sizeof(buffer); read_entry(fp, buffer, &buflen); if (buflen == 0) { /* More entries? */ break; } hp = (struct host *) smalloc(sizeof(struct host)); /* * Get individual info */ hp->linkcount = 3; /* hp will be put in 3 hash tables */ /* don't free hp until linkcount==0*/ hp->flags.vm_auto = TRUE; bcopy(vm_rfc1048, hp->vm_cookie, 4); if (process_entry(hp, buffer) < 0) { free_host(hp); continue; } if (hp->flags.htype && hp->flags.haddr) { hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype)); if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) { report(LOG_ERR, "duplicate hardware address: %s\n", haddrtoa(hp->haddr, hp->htype)); free_host(hp); continue; } } if (hp->flags.iaddr) { hashcode = hash_HashFunction(&(hp->iaddr.s_addr), 4); if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) { report(LOG_ERR, "hash_Insert() failed on IP address insertion\n"); } } hashcode = hash_HashFunction(hp->hostname, strlen(hp->hostname)); if (hash_Insert(nmhashtable, hashcode, nullcmp, hp->hostname, hp) < 0) { report(LOG_ERR, "hash_Insert() failed on insertion of hostname \"%s\"\n", hp->hostname); } nhosts++; } done: fclose(fp); if (debug) { report(LOG_INFO, "read %d entries from \"%s\"\n", nhosts, bootptab); } return 0; } PRIVATE char *strdup(str) char *str; { char *p; if ((p = smalloc(strlen(str) + 1)) == NULL) return NULL; strcpy(p, str); return p; } /* * Read an entire host entry from the file pointed to by "fp" and insert it * into the memory pointed to by "buffer". Leading whitespace and comments * starting with "#" are ignored (removed). Backslashes (\) always quote * the next character except that newlines preceeded by a backslash cause * line-continuation onto the next line. The entry is terminated by a * newline character which is not preceeded by a backslash. Sequences * surrounded by double quotes are taken literally (including newlines, but * not backslashes). * * The "bufsiz" parameter points to an unsigned int which specifies the * maximum permitted buffer size. Upon return, this value will be replaced * with the actual length of the entry (not including the null terminator). * * This code is a little scary. . . . I don't like using gotos in C * either, but I first wrote this as an FSM diagram and gotos seemed like * the easiest way to implement it. Maybe later I'll clean it up. */ PRIVATE void read_entry(fp, buffer, bufsiz) FILE *fp; char *buffer; unsigned *bufsiz; { int c, length; length = 0; /* * Eat whitespace, blank lines, and comment lines. */ top: c = fgetc(fp); if (c < 0) { goto done; /* Exit if end-of-file */ } if (isspace(c)) { goto top; /* Skip over whitespace */ } if (c == '#') { while (TRUE) { /* Eat comments after # */ c = fgetc(fp); if (c < 0) { goto done; /* Exit if end-of-file */ } if (c == '\n') { goto top; /* Try to read the next line */ } } } ungetc(c, fp); /* Other character, push it back to reprocess it */ /* * Now we're actually reading a data entry. Get each character and * assemble it into the data buffer, processing special characters like * double quotes (") and backslashes (\). */ mainloop: c = fgetc(fp); switch (c) { case EOF: case '\n': goto done; /* Exit on EOF or newline */ case '\\': c = fgetc(fp); /* Backslash, read a new character */ if (c < 0) { goto done; /* Exit on EOF */ } *buffer++ = c; /* Store the literal character */ length++; if (length < *bufsiz - 1) { goto mainloop; } else { goto done; } case '"': *buffer++ = '"'; /* Store double-quote */ length++; if (length >= *bufsiz - 1) { goto done; } while (TRUE) { /* Special quote processing loop */ c = fgetc(fp); switch (c) { case EOF: goto done; /* Exit on EOF . . . */ case '"': *buffer++ = '"'; /* Store matching quote */ length++; if (length < *bufsiz - 1) { goto mainloop; /* And continue main loop */ } else { goto done; } case '\\': if ((c = fgetc(fp)) < 0) { /* Backslash */ goto done; /* EOF. . . .*/ } /* else fall through */ default: *buffer++ = c; /* Other character, store it */ length++; if (length >= *bufsiz - 1) { goto done; } } } case ':': *buffer++ = c; /* Store colons */ length++; if (length >= *bufsiz - 1) { goto done; } colonloop: c = fgetc(fp); /* But remove whitespace after them */ if ((c < 0) || (c == '\n')) { goto done; } if (isspace(c)) { goto colonloop; /* Skip whitespace */ } if (c == '\\') { /* Backslash quotes next character */ c = fgetc(fp); if (c < 0) { goto done; } if (c == '\n') { goto top; /* Backslash-newline continuation */ } } /* fall through if "other" character */ default: *buffer++ = c; /* Store other characters */ length++; if (length >= *bufsiz - 1) { goto done; } } goto mainloop; /* Keep going */ done: *buffer = '\0'; /* Terminate string */ *bufsiz = length; /* Tell the caller its length */ } /* * Parse out all the various tags and parameters in the host entry pointed * to by "src". Stuff all the data into the appropriate fields of the * host structure pointed to by "host". If there is any problem with the * entry, an error message is reported via report(), no further processing * is done, and -1 is returned. Successful calls return 0. * * (Some errors probably shouldn't be so completely fatal. . . .) */ PRIVATE int process_entry(host, src) struct host *host; char *src; { int retval; if (!host || *src == '\0') { return -1; } host->hostname = get_str(&src); if (!goodname(host->hostname)) { report(LOG_ERR, "bad hostname: \"%s\"\n", host->hostname); del_string(&(host->hostname)); return -1; } adjust(&src); while (TRUE) { retval = eval_symbol(&src, host); switch (retval) { case SUCCESS: break; case E_SYNTAX_ERROR: report(LOG_ERR, "syntax error in entry for host \"%s\"\n", host->hostname); return -1; case E_UNKNOWN_SYMBOL: report(LOG_ERR, "unknown symbol in entry for host \"%s\"\n", host->hostname); return -1; case E_BAD_IPADDR: report(LOG_ERR, "bad IP address for host \"%s\"\n", host->hostname); return -1; case E_BAD_HADDR: report(LOG_ERR, "bad hardware address for host \"%s\"\n", host->hostname); return -1; case E_BAD_SMASK: report(LOG_ERR, "bad subnet mask for host \"%s\"\n", host->hostname); return -1; case E_BAD_TIMEOFF: report(LOG_ERR, "bad time offset for host \"%s\"\n", host->hostname); return -1; case E_BAD_VM_COOKIE: report(LOG_ERR, "bad vendor magic cookie for host \"%s\"\n", host->hostname); return -1; case E_BAD_HTYPE: report(LOG_ERR, "bad hardware type for host \"%s\"\n", host->hostname); return -1; case E_BAD_BOOT_SERVER: report(LOG_ERR, "bad boot server address for host \"%s\"\n", host->hostname); return -1; case E_END_OF_ENTRY: default: #if 0 /* * For now, don't try to make-up a subnet mask if one * wasn't specified. * * This algorithm is also not entirely correct. */ if (!(hp->flags.subnet_mask)) { /* * Try to deduce the subnet mask from the network class */ value = (ntohl(value) >> 30) & 0x03; switch (value) { case 0: case 1: hp->subnet_mask.s_addr = htonl(0xFF000000L); break; case 2: hp->subnet_mask.s_addr = htonl(0xFFFF0000L); break; case 3: hp->subnet_mask.s_addr = htonl(0xFFFFFF00L); break; } hp->flags.subnet_mask = TRUE; } #endif /* * And now we're done with this entry */ return 0; } adjust(&src); } } /* * Evaluate the two-character tag symbol pointed to by "symbol" and place * the data in the structure pointed to by "hostinfo". The pointer pointed to * by "symbol" is updated to point past the source string (but may not * point to the next tag entry). * * Obviously, this need a few more comments. . . . */ PRIVATE eval_symbol(symbol, hp) char **symbol; struct host *hp; { char tmpstr[MAXSTRINGLEN]; byte *tmphaddr, *ustr; char *ss; struct symbolmap *symbolptr; u_long value; long timeoff; int i, numsymbols; unsigned len; int optype; /* Indicates boolean, addition, or deletion */ if ((*symbol)[0] == '\0') { return E_END_OF_ENTRY; } if ((*symbol)[0] == ':') { return SUCCESS; } if ((*symbol)[0] == 'T') { /* generic symbol */ (*symbol)++; value = get_u_long(symbol); eat_whitespace(symbol); if ((*symbol)[0] != '=') { return E_SYNTAX_ERROR; } (*symbol)++; if (!(hp->generic)) hp->generic = (struct bindata *)smalloc(sizeof(struct bindata)); process_generic(symbol, &(hp->generic), (byte) (value & 0xFF)); hp->flags.generic = TRUE; return SUCCESS; } eat_whitespace(symbol); /* * Determine the type of operation to be done on this symbol */ switch ((*symbol)[2]) { case '=': optype = OP_ADDITION; break; case '@': optype = OP_DELETION; break; case ':': case '\0': optype = OP_BOOLEAN; break; default: return E_SYNTAX_ERROR; } symbolptr = symbol_list; numsymbols = sizeof(symbol_list) / sizeof(struct symbolmap); for (i = 0; i < numsymbols; i++) { if (((symbolptr->symbol)[0] == (*symbol)[0]) && ((symbolptr->symbol)[1] == (*symbol)[1])) { break; } symbolptr++; } if (i >= numsymbols) { return E_UNKNOWN_SYMBOL; } /* * Skip past the = or @ character (to point to the data) if this * isn't a boolean operation. For boolean operations, just skip * over the two-character tag symbol (and nothing else. . . .). */ (*symbol) += (optype == OP_BOOLEAN) ? 2 : 3; switch (symbolptr->symbolcode) { case SYM_BOOTFILE: switch (optype) { case OP_ADDITION: if (ss = get_str(symbol)) { if (hp->bootfile) del_string(&(hp->bootfile)); hp->bootfile = ss; hp->flags.bootfile = TRUE; } break; case OP_DELETION: if (hp->bootfile) del_string(&(hp->bootfile)); hp->bootfile = NULL; hp->flags.bootfile = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_COOKIE_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.cookie_server && hp->cookie_server) { del_iplist(&(hp->cookie_server)); } hp->cookie_server = get_addresses(symbol); hp->flags.cookie_server = TRUE; break; case OP_DELETION: if (hp->flags.cookie_server && hp->cookie_server) { del_iplist(&(hp->cookie_server)); } hp->cookie_server = NULL; hp->flags.cookie_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_DOMAIN_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.domain_server && hp->domain_server) { del_iplist(&(hp->domain_server)); } hp->domain_server = get_addresses(symbol); hp->flags.domain_server = TRUE; break; case OP_DELETION: if (hp->flags.domain_server && hp->domain_server) { del_iplist(&(hp->domain_server)); } hp->domain_server = NULL; hp->flags.domain_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_GATEWAY: switch (optype) { case OP_ADDITION: if (hp->flags.gateway && hp->gateway) { del_iplist(&(hp->gateway)); } hp->gateway = get_addresses(symbol); hp->flags.gateway = TRUE; break; case OP_DELETION: if (hp->flags.gateway && hp->gateway) { del_iplist(&(hp->gateway)); } hp->gateway = NULL; hp->flags.gateway = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_HADDR: switch (optype) { case OP_ADDITION: if (hp->flags.htype && hp->htype && (tmphaddr = prs_haddr(symbol, hp->htype))) { bcopy(tmphaddr, hp->haddr, haddrlength(hp->htype)); hp->flags.haddr = TRUE; } else { return E_BAD_HADDR; } break; case OP_DELETION: hp->flags.haddr = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_HOMEDIR: switch (optype) { case OP_ADDITION: if (ss = get_str(symbol)) { if (hp->homedir) del_string(&(hp->homedir)); hp->homedir = ss; hp->flags.homedir = TRUE; } break; case OP_DELETION: if (hp->homedir) del_string((hp->homedir)); hp->homedir = NULL; hp->flags.homedir = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_HTYPE: switch (optype) { case OP_ADDITION: value = 0L; /* Assume an illegal value */ eat_whitespace(symbol); if (isdigit(**symbol)) { value = get_u_long(symbol); } else { len = sizeof(tmpstr); (void) get_string(symbol, tmpstr, &len); makelower(tmpstr); numsymbols = sizeof(htnamemap) / sizeof(struct htypename); for (i = 0; i < numsymbols; i++) { if (!strcmp(htnamemap[i].name, tmpstr)) { break; } } if (i < numsymbols) { value = htnamemap[i].htype; } } if (!((0 < value) && (value <= MAXHTYPES))) { /* alt */ return E_BAD_HTYPE; } hp->htype = (byte) (value & 0xFF); hp->flags.htype = TRUE; break; case OP_DELETION: hp->flags.htype = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_IMPRESS_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.impress_server && hp->impress_server) { del_iplist(&(hp->impress_server)); } hp->impress_server = get_addresses(symbol); hp->flags.impress_server = TRUE; break; case OP_DELETION: if (hp->flags.impress_server && hp->impress_server) { del_iplist(&(hp->impress_server)); } hp->impress_server = NULL; hp->flags.impress_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_IPADDR: switch (optype) { case OP_ADDITION: if (prs_inetaddr(symbol, &value) < 0) { return E_BAD_IPADDR; } else { hp->iaddr.s_addr = value; hp->flags.iaddr = TRUE; } break; case OP_DELETION: hp->flags.iaddr = FALSE; break; case OP_BOOLEAN: { struct hostent * host_ptr; if(!hp->hostname) return E_SYNTAX_ERROR; if ((host_ptr = gethostbyname(hp->hostname)) == NULL) return E_SYNTAX_ERROR; else { value = *((unsigned long *)host_ptr->h_addr); hp->iaddr.s_addr = value; hp->flags.iaddr = TRUE; } } } break; case SYM_LOG_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.log_server && hp->log_server) { del_iplist(&(hp->log_server)); } hp->log_server = get_addresses(symbol); hp->flags.log_server = TRUE; break; case OP_DELETION: if (hp->flags.log_server && hp->log_server) { del_iplist(&(hp->log_server)); } hp->log_server = NULL; hp->flags.log_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_LPR_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.lpr_server && hp->lpr_server) { del_iplist(&(hp->lpr_server)); } hp->lpr_server = get_addresses(symbol); hp->flags.lpr_server = TRUE; break; case OP_DELETION: if (hp->flags.lpr_server && hp->lpr_server) { del_iplist(&(hp->lpr_server)); } hp->lpr_server = NULL; hp->flags.lpr_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_NAME_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.name_server && hp->name_server) { del_iplist(&(hp->name_server)); } hp->name_server = get_addresses(symbol); hp->flags.name_server = TRUE; break; case OP_DELETION: if (hp->flags.name_server && hp->name_server) { del_iplist(&(hp->name_server)); } hp->name_server = NULL; hp->flags.name_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_RLP_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.rlp_server && hp->rlp_server) { del_iplist(&(hp->rlp_server)); } hp->rlp_server = get_addresses(symbol); hp->flags.rlp_server = TRUE; break; case OP_DELETION: if (hp->flags.rlp_server && hp->rlp_server) { del_iplist(&(hp->rlp_server)); } hp->rlp_server = NULL; hp->flags.rlp_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_SUBNET_MASK: switch (optype) { case OP_ADDITION: if (prs_inetaddr(symbol, &value) < 0) { return E_BAD_SMASK; } else { hp->subnet_mask.s_addr = value; hp->flags.subnet_mask = TRUE; } break; case OP_DELETION: hp->flags.subnet_mask = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_TIME_OFFSET: switch (optype) { case OP_ADDITION: len = sizeof(tmpstr); (void) get_string(symbol, tmpstr, &len); if (!strncmp(tmpstr, "auto", 4)) { hp->flags.timeoff_auto = TRUE; hp->flags.time_offset = TRUE; } else { if (sscanf(tmpstr, "%ld", &timeoff) != 1) { return E_BAD_TIMEOFF; } else { hp->time_offset = timeoff; hp->flags.timeoff_auto = FALSE; hp->flags.time_offset = TRUE; } } break; case OP_DELETION: hp->flags.time_offset = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_TIME_SERVER: switch (optype) { case OP_ADDITION: if (hp->flags.time_server && hp->time_server) { del_iplist(&(hp->time_server)); } hp->time_server = get_addresses(symbol); hp->flags.time_server = TRUE; break; case OP_DELETION: if (hp->flags.time_server && hp->time_server) { del_iplist(&(hp->time_server)); } hp->time_server = NULL; hp->flags.time_server = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_VENDOR_MAGIC: switch (optype) { case OP_ADDITION: if (!strncmp(*symbol, "auto", 4)) { hp->flags.vm_auto = TRUE; /* Make it auto */ } else if (!strncmp(*symbol, "rfc1048", 7) || !strncmp(*symbol, "rfc1084", 7)) { hp->flags.vm_auto = FALSE; /* Make it manual */ bcopy(vm_rfc1048, hp->vm_cookie, 4); } else if (!strncmp(*symbol, "cmu", 3)) { hp->flags.vm_auto = FALSE; /* Make it manual */ bcopy(vm_cmu, hp->vm_cookie, 4); } else { if (prs_inetaddr(symbol, &value) < 0) { return E_BAD_VM_COOKIE; } hp->flags.vm_auto = FALSE; /* Make it manual */ ustr = hp->vm_cookie; insert_u_long(value, &ustr); } hp->flags.vendor_magic = TRUE; break; case OP_DELETION: hp->flags.vendor_magic = FALSE; break; case OP_BOOLEAN: hp->flags.vm_auto = TRUE; hp->flags.vendor_magic = TRUE; break; } break; case SYM_SIMILAR_ENTRY: switch (optype) { case OP_ADDITION: fill_defaults(hp, symbol); break; default: return E_SYNTAX_ERROR; } break; case SYM_NAME_SWITCH: switch (optype) { case OP_ADDITION: return E_SYNTAX_ERROR; case OP_DELETION: hp->flags.send_name = FALSE; hp->flags.name_switch = FALSE; break; case OP_BOOLEAN: hp->flags.send_name = TRUE; hp->flags.name_switch = TRUE; break; } break; case SYM_BOOTSIZE: switch (optype) { case OP_ADDITION: if (!strncmp(*symbol, "auto", 4)) { hp->flags.bootsize = TRUE; hp->flags.bootsize_auto = TRUE; } else { hp->bootsize = (unsigned int) get_u_long(symbol); hp->flags.bootsize = TRUE; hp->flags.bootsize_auto = FALSE; } break; case OP_DELETION: hp->flags.bootsize = FALSE; break; case OP_BOOLEAN: hp->flags.bootsize = TRUE; hp->flags.bootsize_auto = TRUE; break; } break; case SYM_BOOT_SERVER: switch (optype) { case OP_ADDITION: if (prs_inetaddr(symbol, &value) < 0) { return E_BAD_BOOT_SERVER; } else { hp->bootserver.s_addr = value; hp->flags.bootserver = TRUE; } break; case OP_DELETION: hp->flags.bootserver = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; case SYM_BOOT_TYPE: /* This tag is no longer valid. Therefore, */ /* we will ignore it. */ break; case SYM_DUMB_TERMINAL : switch (optype) { case OP_ADDITION: return E_SYNTAX_ERROR; case OP_DELETION: hp->flags.dumb_terminal = FALSE; break; case OP_BOOLEAN: hp->flags.dumb_terminal = TRUE; break; } break; case SYM_DOMAIN: switch (optype) { case OP_ADDITION: if (ss = get_str(symbol)) { if (hp->domain) del_string(&(hp->domain)); hp->domain = ss; hp->flags.domain = TRUE; } break; case OP_DELETION: if (hp->domain) del_string((hp->domain)); hp->domain = NULL; hp->flags.domain = FALSE; break; case OP_BOOLEAN: return E_SYNTAX_ERROR; } break; default: return E_UNKNOWN_SYMBOL; } return SUCCESS; } /* * Read a string from the buffer indirectly pointed to through "src" and * move it into the buffer pointed to by "dest". A pointer to the maximum * allowable length of the string (including null-terminator) is passed as * "length". The actual length of the string which was read is returned in * the unsigned integer pointed to by "length". This value is the same as * that which would be returned by applying the strlen() function on the * destination string (i.e the terminating null is not counted as a * character). Trailing whitespace is removed from the string. For * convenience, the function returns the new value of "dest". * * The string is read until the maximum number of characters, an unquoted * colon (:), or a null character is read. The return string in "dest" is * null-terminated. */ PRIVATE char *get_string(src, dest, length) char **src, *dest; unsigned *length; { int n, len, quoteflag; quoteflag = FALSE; n = 0; len = *length - 1; while ((n < len) && (**src)) { if (!quoteflag && (**src == ':')) { break; } if (**src == '"') { (*src)++; quoteflag = !quoteflag; continue; } if (**src == '\\') { (*src)++; if (! **src) { break; } } *dest++ = *(*src)++; n++; } /* * Remove that troublesome trailing whitespace. . . */ while ((n > 0) && isspace(dest[-1])) { dest--; n--; } *dest = '\0'; *length = n; return dest; } /* * Read the string indirectly pointed to by "src", update the caller's * pointer, and return a pointer to a malloc'ed shared_string structure * containing the string. * * The string is read using the same rules as get_string() above. */ PRIVATE char *get_str(src) char **src; { char retstring[MAXSTRINGLEN]; char *p; unsigned length; length = sizeof(retstring); (void) get_string(src, retstring, &length); p = (char *)smalloc(length + 1); strcpy(p, retstring); return p; } /* * Load RFC1048 generic information directly into a memory buffer. * * "src" indirectly points to the ASCII representation of the generic data. * "dest" points to a string structure which is updated to point to a new * string with the new data appended to the old string. The old string is * freed. * * The given tag value is inserted with the new data. * * The data may be represented as either a stream of hexadecimal numbers * representing bytes (any or all bytes may optionally start with '0x' and * be separated with periods ".") or as a quoted string of ASCII * characters (the quotes are required). */ PRIVATE void process_generic(src, dest, tagvalue) char **src; struct bindata **dest; byte tagvalue; { byte tmpbuf[MAXBUFLEN]; byte tmpstring[MAXBUFLEN]; byte *str; struct bindata *bdata; int newlength, oldlength; struct hostent *p; str = tmpbuf; *str++ = tagvalue; /* Store tag value */ str++; /* Skip over length field */ if ((*src)[0] == '"') { /* ASCII data */ newlength = sizeof(tmpbuf) - 2; /* Set maximum allowed length */ if ( tagvalue == TAG_XDMCP_IP ) /* If it's an XDMCP xdm host name, convert it to an ip address */ { get_string(src, tmpstring, &newlength); if ((p = gethostbyname(tmpstring))==NULL ) { report(LOG_ERR, "cannot get XDM host %s ip address",str); for(newlength=0; newlength<4; newlength++ ,*str++ = 0); } else { char *cp = p->h_addr; for(newlength=0; newlength<4; newlength++ ) { *str++ = *cp++; } } } else (void) get_string(src, str, &newlength); } else { /* Numeric data */ newlength = 0; while (newlength < sizeof(tmpbuf) - 2) { if (interp_byte(src, str++) < 0) { break; } else { newlength++; } if (**src == '.') { (*src)++; } } } tmpbuf[1] = (byte) (newlength & 0xFF); oldlength = ((*dest)->length); bdata = (struct bindata *) smalloc(sizeof(struct bindata) + oldlength + newlength + 1); if (oldlength > 0) { bcopy((*dest)->data, bdata->data, oldlength); } bcopy(tmpbuf, bdata->data + oldlength, newlength + 2); bdata->length = oldlength + newlength + 2; if (*dest) del_bindata(dest); *dest = bdata; } /* * Verify that the given string makes sense as a hostname (according to * Appendix 1, page 29 of RFC882). * * Return TRUE for good names, FALSE otherwise. */ PRIVATE boolean goodname(hostname) register char *hostname; { do { #ifdef ORG if (!isalpha(*hostname++)) { /* First character must be a letter */ return FALSE; } #else if (!isalnum(*hostname++)) { /* First character must be a letter */ return FALSE; } #endif while (isalnum(*hostname) || (*hostname == '-') || (*hostname == '_')) { hostname++; /* Alphanumeric or a hyphen */ } if (!isalnum(hostname[-1])) { /* Last must be alphanumeric */ return FALSE; } if (*hostname == '\0') { /* Done? */ return TRUE; } } while (*hostname++ == '.'); /* Dot, loop for next label */ return FALSE; /* If it's not a dot, lose */ } /* * Null compare function -- always returns FALSE so an element is always * inserted into a hash table (i.e. there is never a collision with an * existing element). */ PRIVATE boolean nullcmp(host1, host2) struct host *host1, *host2; { return FALSE; } /* * Function for comparing a string with the hostname field of a host * structure. */ PRIVATE boolean nmcmp(name, hp) char *name; struct host *hp; { return !strcmp(name, hp->hostname); } /* * Compare function to determine whether two hardware addresses are * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE * otherwise. * * If the hardware addresses of "host1" and "host2" are identical, but * they are on different IP subnets, this function returns FALSE. * * This function is used when inserting elements into the hardware address * hash table. */ PRIVATE boolean hwinscmp(host1, host2) struct host *host1, *host2; { if (host1->htype != host2->htype) { return FALSE; } if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) { return FALSE; } if (host1->subnet_mask.s_addr == host1->subnet_mask.s_addr) { if ((host1->iaddr.s_addr & host1->subnet_mask.s_addr) != (host2->iaddr.s_addr & host2->subnet_mask.s_addr)) { return FALSE; } } return TRUE; } /* * Process the "similar entry" symbol. * * The host specified as the value of the "tc" symbol is used as a template * for the current host entry. Symbol values not explicitly set in the * current host entry are inferred from the template entry. */ PRIVATE void fill_defaults(hp, src) struct host *hp; char **src; { unsigned tlen, hashcode; struct host *hp2, thp; char tstring[MAXSTRINGLEN]; tlen = sizeof(tstring); (void) get_string(src, tstring, &tlen); #ifdef ORG if (goodname(tstring)) { hashcode = hash_HashFunction(tstring, tlen); hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring); } else { thp.iaddr.s_addr = inet_addr(tstring); hashcode = hash_HashFunction(thp.iaddr.s_addr, 4); hp2 = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, &thp); } #else if((thp.iaddr.s_addr = inet_addr(tstring)) != -1) { hashcode = hash_HashFunction(thp.iaddr.s_addr, 4); hp2 = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, &thp); } else { hashcode = hash_HashFunction(tstring, tlen); hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring); } #endif if (hp2 == NULL) { report(LOG_ERR, "can't find tc=\"%s\"\n", tstring); } else { /* * Assignments inside "if" conditionals are intended here. */ if (!hp->flags.cookie_server) if (hp->flags.cookie_server = hp2->flags.cookie_server) hp->cookie_server = iplistdup(hp2->cookie_server); if (!hp->flags.domain_server) if (hp->flags.domain_server = hp2->flags.domain_server) hp->domain_server = iplistdup(hp2->domain_server); if (!hp->flags.gateway) if (hp->flags.gateway = hp2->flags.gateway) hp->gateway = iplistdup(hp2->gateway); if (!hp->flags.impress_server) if (hp->flags.impress_server = hp2->flags.impress_server) hp->impress_server = iplistdup(hp2->impress_server); if (!hp->flags.log_server) if (hp->flags.log_server = hp2->flags.log_server) hp->log_server = iplistdup(hp2->log_server); if (!hp->flags.lpr_server) if (hp->flags.lpr_server = hp2->flags.lpr_server) hp->lpr_server = iplistdup(hp2->lpr_server); if (!hp->flags.name_server) if (hp->flags.name_server = hp2->flags.name_server) hp->name_server = iplistdup(hp2->name_server); if (!hp->flags.rlp_server) if (hp->flags.rlp_server = hp2->flags.rlp_server) hp->rlp_server = iplistdup(hp2->rlp_server); if (!hp->flags.time_server) if (hp->flags.time_server = hp2->flags.time_server) hp->time_server = iplistdup(hp2->time_server); if (!hp->flags.homedir) if (hp->flags.homedir = hp2->flags.homedir) hp->homedir = strdup(hp2->homedir); if (!hp->flags.bootfile) if (hp->flags.bootfile = hp2->flags.bootfile) hp->bootfile = strdup(hp2->bootfile); if (!hp->flags.generic) if (hp->flags.generic = hp2->flags.generic) hp->generic = bindup(hp2->generic); if (!hp->flags.vendor_magic) { hp->flags.vm_auto = hp2->flags.vm_auto; if (hp->flags.vendor_magic = hp2->flags.vendor_magic) { bcopy(hp2->vm_cookie, hp->vm_cookie, 4); } } if (!hp->flags.name_switch) { if (hp->flags.name_switch = hp2->flags.name_switch) { hp->flags.send_name = hp2->flags.send_name; } } if (!hp->flags.htype) { if (hp->flags.htype = hp2->flags.htype) { hp->htype = hp2->htype; } } if (!hp->flags.time_offset) { if (hp->flags.time_offset = hp2->flags.time_offset) { hp->flags.timeoff_auto = hp2->flags.timeoff_auto; hp->time_offset = hp2->time_offset; } } if (!hp->flags.subnet_mask) { if (hp->flags.subnet_mask = hp2->flags.subnet_mask) { hp->subnet_mask.s_addr = hp2->subnet_mask.s_addr; } } if (!hp->flags.bootsize) { if (hp->flags.bootsize = hp2->flags.bootsize) { hp->flags.bootsize_auto = hp2->flags.bootsize_auto; hp->bootsize = hp2->bootsize; } } /* This code was added specifically for the X-Station. This enhancement */ /* allows the user to define the sa tag in either the Network Type */ /* definition in the /etc/bootptab file or in the X-Station definition */ /* in the /etc/bootptab file. */ if (!hp->flags.bootserver) { if (hp->flags.bootserver = hp2->flags.bootserver) { hp->bootserver.s_addr = hp2->bootserver.s_addr; } } if (!hp->flags.dumb_terminal){ hp->flags.dumb_terminal = hp2->flags.dumb_terminal; } if (!hp->flags.domain) if (hp->flags.domain = hp2->flags.domain) hp->domain = strdup(hp2->domain); } } /* * This function adjusts the caller's pointer to point just past the * first-encountered colon. If it runs into a null character, it leaves * the pointer pointing to it. */ PRIVATE void adjust(s) char **s; { register char *t; t = *s; while (*t && (*t != ':')) { t++; } if (*t) { t++; } *s = t; } /* * This function adjusts the caller's pointer to point to the first * non-whitespace character. If it runs into a null character, it leaves * the pointer pointing to it. */ PRIVATE void eat_whitespace(s) char **s; { register char *t; t = *s; while (*t && isspace(*t)) { t++; } *s = t; } /* * This function converts the given string to all lowercase. */ PRIVATE void makelower(s) char *s; { while (*s) { if (isupper(*s)) { *s = tolower(*s); } s++; } } /* * * N O T E : * * In many of the functions which follow, a parameter such as "src" or * "symbol" is passed as a pointer to a pointer to something. This is * done for the purpose of letting the called function update the * caller's copy of the parameter (i.e. to effect call-by-reference * parameter passing). The value of the actual parameter is only used * to locate the real parameter of interest and then update this indirect * parameter. * * I'm sure somebody out there won't like this. . . . * * */ /* * "src" points to a character pointer which points to an ASCII string of * whitespace-separated IP addresses. A pointer to an in_addr_list * structure containing the list of addresses is returned. NULL is * returned if no addresses were found at all. The pointer pointed to by * "src" is updated to point to the first non-address (illegal) character. */ PRIVATE struct in_addr_list *get_addresses(src) char **src; { struct in_addr tmpaddrlist[MAXINADDRS]; struct in_addr *address1, *address2; struct in_addr_list *result; unsigned addrcount, totalsize; address1 = tmpaddrlist; for (addrcount = 0; addrcount < MAXINADDRS; addrcount++) { while (**src && isspace(**src)) { /* Skip whitespace */ (*src)++; } if (! **src) { /* Quit if nothing more */ break; } if (prs_inetaddr(src, &(address1->s_addr)) < 0) { break; } address1++; /* Point to next address slot */ } if (addrcount < 1) { result = NULL; } else { totalsize = sizeof(struct in_addr_list) + (addrcount - 1) * sizeof(struct in_addr); result = (struct in_addr_list *) smalloc(totalsize); result->addrcount = addrcount; address1 = tmpaddrlist; address2 = result->addr; for (; addrcount > 0; addrcount--) { address2->s_addr = address1->s_addr; address1++; address2++; } } return result; } /* * prs_inetaddr(src, result) * * "src" is a value-result parameter; the pointer it points to is updated * to point to the next data position. "result" points to an unsigned long * in which an address is returned. * * This function parses the IP address string in ASCII "dot notation" pointed * to by (*src) and places the result (in network byte order) in the unsigned * long pointed to by "result". For malformed addresses, -1 is returned, * (*src) points to the first illegal character, and the unsigned long pointed * to by "result" is unchanged. Successful calls return 0. */ PRIVATE prs_inetaddr(src, result) char **src; u_long *result; { register u_long value; u_long parts[4], *pp = parts; int n; if (!isdigit(**src)) { return -1; } loop: value = get_u_long(src); if (**src == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16-bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 4) { return (-1); } *pp++ = value; (*src)++; goto loop; } /* * Check for trailing characters. */ if (**src && !(isspace(**src) || (**src == ':'))) { return (-1); } *pp++ = value; /* * Construct the address according to * the number of parts specified. */ n = pp - parts; switch (n) { case 1: /* a -- 32 bits */ value = parts[0]; break; case 2: /* a.b -- 8.24 bits */ value = (parts[0] << 24) | (parts[1] & 0xFFFFFF); break; case 3: /* a.b.c -- 8.8.16 bits */ value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | (parts[2] & 0xFFFF); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | ((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF); break; default: return (-1); } *result = htonl(value); return (0); } /* * "src" points to a pointer which in turn points to a hexadecimal ASCII * string. This string is interpreted as a hardware address and returned * as a pointer to the actual hardware address, represented as an array of * bytes. * * The ASCII string must have the proper number of digits for the specified * hardware type (e.g. twelve digits for a 48-bit Ethernet address). * Two-digit sequences (bytes) may be separated with periods (.) and/or * prefixed with '0x' for readability, but this is not required. * * For bad addresses, the pointer which "src" points to is updated to point * to the start of the first two-digit sequence which was bad, and the * function returns a NULL pointer. */ PRIVATE byte *prs_haddr(src, htype) char **src; byte htype; { static byte haddr[MAXHADDRLEN]; byte *hptr; unsigned hlen; hlen = haddrlength(htype); /* Get length of this address type */ hptr = haddr; while (hptr < haddr + hlen) { if (**src == '.') { (*src)++; } if (interp_byte(src, hptr++) < 0) { return NULL; } } return haddr; } /* * "src" is a pointer to a character pointer which in turn points to a * hexadecimal ASCII representation of a byte. This byte is read, the * character pointer is updated, and the result is deposited into the * byte pointed to by "retbyte". * * The usual '0x' notation is allowed but not required. The number must be * a two digit hexadecimal number. If the number is invalid, "src" and * "retbyte" are left untouched and -1 is returned as the function value. * Successful calls return 0. */ PRIVATE int interp_byte(src, retbyte) char **src; byte *retbyte; { int v = 0; char xdigit; if ((*src)[0] == '0' && (*src)[1] == 'x' || (*src)[1] == 'X') { (*src) += 2; /* allow 0x for hex, but don't require it */ } *retbyte = 0; while((isxdigit(xdigit = *src[0])) && ( v < 2 )) { if ( isdigit(xdigit)) xdigit -= '0'; else if ( islower(xdigit) ) xdigit = xdigit - 'a' +10 ; else if ( isupper(xdigit)) xdigit = xdigit - 'A' + 10 ; *retbyte = 16* (*retbyte) + xdigit ; (*src)++; v++; } if ( !v ) return -1; *retbyte &= 0xFF; return 0; } /* * The parameter "src" points to a character pointer which points to an * ASCII string representation of an unsigned number. The number is * returned as an unsigned long and the character pointer is updated to * point to the first illegal character. */ PRIVATE u_long get_u_long(src) char **src; { register u_long value, base; char c; /* * Collect number up to first illegal character. Values are specified * as for C: 0x=hex, 0=octal, other=decimal. */ value = 0; base = 10; if (**src == '0') { base = 8, (*src)++; } if (**src == 'x' || **src == 'X') { base = 16, (*src)++; } while (c = **src) { if (isdigit(c)) { value = (value * base) + (c - '0'); (*src)++; continue; } if (base == 16 && isxdigit(c)) { value = (value << 4) + ((c & ~32) + 10 - 'A'); (*src)++; continue; } break; } return value; } /* * Routines for deletion of data associated with the main data structure. */ /* * Frees the entire host data structure given. Does nothing if the passed * pointer is NULL. */ PRIVATE void free_host(hostptr) struct host *hostptr; { if (hostptr) { del_iplist(&(hostptr->cookie_server)); del_iplist(&(hostptr->domain_server)); del_iplist(&(hostptr->gateway)); del_iplist(&(hostptr->impress_server)); del_iplist(&(hostptr->log_server)); del_iplist(&(hostptr->lpr_server)); del_iplist(&(hostptr->name_server)); del_iplist(&(hostptr->rlp_server)); del_iplist(&(hostptr->time_server)); del_string(&(hostptr->hostname)); del_string(&(hostptr->homedir)); del_string(&(hostptr->bootfile)); del_bindata(&(hostptr->generic)); del_string(&(hostptr->domain)); if (! (--(hostptr->linkcount))) free((char *) hostptr); } } /* * Decrements the linkcount on the given IP address data structure. If the * linkcount goes to zero, the memory associated with the data is freed. */ PRIVATE void del_iplist(iplist) struct in_addr_list **iplist; { if (*iplist) { free((char *) *iplist); *iplist = NULL; } } /* * malloc a new struct in_addr_list and copy contents of iplist to it. */ PRIVATE struct in_addr_list *iplistdup(iplist) struct in_addr_list *iplist; { struct in_addr_list *ip; if (iplist == NULL) return NULL; if((ip = (struct in_addr_list *)smalloc((iplist->addrcount - 1) * sizeof(struct in_addr) + sizeof(struct in_addr_list))) == NULL) { report(LOG_ERR, "malloc() failed in iplistdup()\n"); return NULL; } ip->addrcount = iplist->addrcount; memcpy(ip->addr, iplist->addr, ip->addrcount * sizeof(struct in_addr)); return ip; } /* * malloc a new struct bindata and copy contents of bdata to it. */ PRIVATE struct bindata *bindup(bdata) struct bindata *bdata; { struct bindata *bd; if (bdata == NULL) return NULL; if((bd = (struct bindata *)smalloc((bdata->length - 1) * sizeof(byte) + sizeof(struct bindata))) == NULL) { report(LOG_ERR, "malloc() failed in bindup()\n"); return NULL; } bd->length = bdata->length; memcpy(bd->data, bdata->data, bd->length * sizeof(byte)); return bd; } /* * Free's a non-null string */ PRIVATE void del_string(stringptr) char **stringptr; { if (*stringptr) { free(*stringptr); *stringptr = NULL; } } /* * free's non-null struct bindata * */ PRIVATE void del_bindata(dataptr) struct bindata **dataptr; { if (*dataptr) { free(*dataptr); *dataptr = NULL; } } /* smalloc() -- safe malloc() * * Always returns a valid pointer (if it returns at all). The allocated * memory is initialized to all zeros. If malloc() returns an error, a * message is printed using the report() function and the program aborts * with a status of 1. */ PRIVATE char *smalloc(nbytes) unsigned nbytes; { char *retvalue; retvalue = malloc(nbytes); if (!retvalue) { report(LOG_ERR, "malloc() failure -- exiting\n"); exit(1); } bzero(retvalue, nbytes); return retvalue; }