1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" /* SMI4.1 1.7 */ 28 29 #include <stdio.h> 30 #include <ndbm.h> 31 #include <netdb.h> 32 #include <sys/types.h> 33 #include <sys/socket.h> 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 #include <string.h> 37 #include <ctype.h> 38 #include <errno.h> 39 40 /* 41 * Filter to convert both IPv4 and IPv6 addresses from /etc/hosts or 42 * /etc/inet/ipnodes files. 43 */ 44 45 /* 46 * Size of buffer for input lines. Add two bytes on input for newline 47 * and terminating NULL. Note that the practical limit for data 48 * storage in ndbm is (PBLKSIZ - 3 * sizeof (short)). Though this 49 * differs from spec 1170 the common industry implementation does 50 * conform to this slightly lower limit. 51 */ 52 53 #define OUTPUTSIZ (PBLKSIZ - 3 * sizeof (short)) 54 #define INPUTSIZ (OUTPUTSIZ + 2) 55 56 static int ipv4 = -1; 57 static char *cmd; 58 int warn = 0; 59 60 static void verify_and_output(const char *key, char *value, int lineno); 61 62 void 63 usage() 64 { 65 fprintf(stderr, "stdhosts [-w] [-n] [in-file]\n"); 66 fprintf(stderr, "\t-w\tprint malformed warning messages.\n"); 67 exit(1); 68 } 69 70 int 71 main(argc, argv) 72 char **argv; 73 { 74 char line[INPUTSIZ]; 75 char adr[INPUTSIZ]; 76 char nadr[INET6_ADDRSTRLEN]; /* Contains normalised address */ 77 const char *nadrp; /* Pointer to the normalised address */ 78 char *trailer; 79 char *commentp; /* Pointer to comment character '#' */ 80 int c; 81 FILE *fp; 82 int lineno = 0; /* Input line counter */ 83 struct in_addr in; /* Used for normalising the IPv4 address */ 84 struct in6_addr in6; /* Used for normalising the IPv6 address */ 85 char *fgetsp; /* Holds return value for fgets() calls */ 86 int endoffile = 0; /* Set when end of file reached */ 87 88 if (cmd = strrchr(argv[0], '/')) 89 ++cmd; 90 else 91 cmd = argv[0]; 92 93 while ((c = getopt(argc, argv, "v:wn")) != -1) { 94 switch (c) { 95 case 'w': /* Send warning messages to stderr */ 96 warn = 1; 97 break; 98 case 'n': 99 ipv4 = 0; 100 break; 101 default: 102 usage(); 103 exit(1); 104 } 105 } 106 107 if (optind < argc) { 108 fp = fopen(argv[optind], "r"); 109 if (fp == NULL) { 110 fprintf(stderr, "%s: can't open %s\n", 111 cmd, argv[optind]); 112 exit(1); 113 } 114 } else 115 fp = stdin; 116 117 while (!endoffile && 118 (fgetsp = fgets(line, sizeof (line), fp)) != NULL) { 119 lineno++; 120 121 /* Check for comments */ 122 if ((commentp = strchr(line, '#')) != NULL) { 123 if ((line[strlen(line) - 1] != '\n') && 124 (strlen(line) >= (sizeof (line) - 1))) { 125 /* 126 * Discard the remainder of the line 127 * until the newline or EOF, then 128 * continue to parse the line. Use 129 * adr[] rather then line[] to 130 * preserve the contents of line[]. 131 */ 132 while ((fgetsp = fgets(adr, sizeof (adr), 133 fp)) != NULL) { 134 if (adr[strlen(adr) - 1] == '\n') 135 break; 136 } 137 if (fgetsp == NULL) 138 endoffile = 1; 139 } 140 /* Terminate line[] at the comment character */ 141 *commentp = '\0'; 142 } else if ((line[strlen(line) - 1] != '\n') && 143 (strlen(line) >= (sizeof (line) - 1))) { 144 /* 145 * Catch long lines but not if this is a short 146 * line with no '\n' at the end of the input. 147 */ 148 if (warn) 149 fprintf(stderr, 150 "%s: Warning: more than %d " 151 "bytes on line %d, ignored\n", 152 cmd, sizeof (line) - 2, lineno); 153 /* 154 * Discard the remaining lines until the 155 * newline or EOF. 156 */ 157 while ((fgetsp = fgets(line, sizeof (line), 158 fp)) != NULL) 159 if (line[strlen(line) - 1] == '\n') 160 break; 161 if (fgetsp == NULL) 162 endoffile = 1; 163 continue; 164 } 165 166 if (sscanf(line, "%s", adr) != 1) { /* Blank line, ignore */ 167 continue; 168 } 169 170 if ((trailer = strpbrk(line, " \t")) == NULL) { 171 if (warn) 172 fprintf(stderr, 173 "%s: Warning: no host names on line %d, " 174 "ignored\n", cmd, lineno); 175 continue; 176 } 177 178 /* 179 * check for valid addresses 180 * 181 * Attempt an ipv4 conversion, this accepts all valid 182 * ipv4 addresses including: 183 * d 184 * d.d 185 * d.d.d 186 * Unfortunately inet_pton() doesn't recognise these. 187 */ 188 189 in.s_addr = inet_addr(adr); 190 if (-1 != (int)in.s_addr) { 191 /* 192 * It's safe not to check return of NULL as 193 * nadrp is checked for validity later. 194 */ 195 nadrp = inet_ntop(AF_INET, &in, nadr, sizeof (nadr)); 196 } else { 197 nadrp = NULL; /* Not a valid IPv4 address */ 198 } 199 200 if (ipv4) { 201 if (nadrp == NULL) { 202 if (warn) 203 fprintf(stderr, 204 "%s: Warning: malformed address on" 205 " line %d, ignored\n", 206 cmd, lineno); 207 continue; 208 } 209 } else { /* v4 or v6 for ipnodes */ 210 if (nadrp == NULL) { 211 if (inet_pton(AF_INET6, adr, &in6) == 1) { 212 nadrp = inet_ntop(AF_INET6, &in6, 213 nadr, sizeof (nadr)); 214 } 215 if (nadrp == NULL) { /* Invalid IPv6 too */ 216 if (warn) 217 fprintf(stderr, 218 "%s: Warning: malformed" 219 " address on" 220 " line %d, ignored\n", 221 cmd, lineno); 222 continue; 223 } 224 } 225 } 226 227 verify_and_output(nadrp, trailer, lineno); 228 229 } /* while */ 230 return (0); 231 /* NOTREACHED */ 232 } 233 234 /* 235 * verify_and_output 236 * 237 * Builds and verifies the output key and value string 238 * 239 * It makes sure these rules are followed: 240 * key + separator + value <= OUTPUTSIZ (for ndbm) 241 * names <= MAXALIASES + 1, ie one canonical name + MAXALIASES aliases 242 * It will also ignore everything after a '#' comment character 243 */ 244 static void 245 verify_and_output(const char *key, char *value, int lineno) 246 { 247 char *p; /* General char pointer */ 248 char *endp; /* Points to the NULL at the end */ 249 char *namep; /* First character of a name */ 250 char tmpbuf[OUTPUTSIZ+1]; /* Buffer before writing out */ 251 char *tmpbufp = tmpbuf; /* Current point in output string */ 252 int n = 0; /* Length of output */ 253 int names = 0; /* Number of names found */ 254 int namelen; /* Length of the name */ 255 256 if (key) { /* Just in case key is NULL */ 257 n = strlen(key); 258 if (n > OUTPUTSIZ) { 259 if (warn) 260 fprintf(stderr, 261 "%s: address too long on " 262 "line %d, line discarded\n", 263 cmd, lineno); 264 return; 265 } 266 memcpy(tmpbufp, key, n+1); /* Plus the '\0' */ 267 tmpbufp += n; 268 } 269 270 if (value) { /* Just in case value is NULL */ 271 p = value; 272 if ((endp = strchr(value, '#')) == 0) /* Ignore # comments */ 273 endp = p + strlen(p); /* Or endp = EOL */ 274 do { 275 /* 276 * Skip white space. Type conversion is 277 * necessary to avoid unfortunate effects of 278 * 8-bit characters appearing negative. 279 */ 280 while ((p < endp) && isspace((unsigned char)*p)) 281 p++; 282 283 if (p == endp) /* End of the string */ 284 break; 285 286 names++; 287 if (names > (MAXALIASES+1)) { /* cname + MAXALIASES */ 288 if (warn) 289 fprintf(stderr, 290 "%s: Warning: too many " 291 "host names on line %d, " 292 "truncating\n", 293 cmd, lineno); 294 break; 295 } 296 297 namep = p; 298 while ((p < endp) && !isspace((unsigned char)*p)) 299 p++; 300 301 namelen = p - namep; 302 n += namelen + 1; /* single white space + name */ 303 *p = '\0'; /* Terminate the name string */ 304 if (n > OUTPUTSIZ) { 305 if (warn) 306 fprintf(stderr, 307 "%s: Warning: %d byte ndbm limit " 308 "reached on line %d, truncating\n", 309 cmd, OUTPUTSIZ, lineno); 310 break; 311 } 312 313 if (names == 1) /* First space is a '\t' */ 314 *tmpbufp++ = '\t'; 315 else 316 *tmpbufp++ = ' '; 317 318 memcpy(tmpbufp, namep, namelen+1); /* Plus the '\0' */ 319 tmpbufp += namelen; 320 321 if (p < endp) 322 p++; /* Skip the added NULL */ 323 324 } while (p < endp); 325 } 326 327 if (names > 0) { 328 fputs(tmpbuf, stdout); 329 fputc('\n', stdout); 330 } else { 331 if (warn) 332 fprintf(stderr, 333 "%s: Warning: no host names on line %d, " 334 "ignored\n", cmd, lineno); 335 } 336 } 337