4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI" /* SMI4.1 1.7 */
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
40 * Filter to convert both IPv4 and IPv6 addresses from /etc/hosts file.
44 * Size of buffer for input lines. Add two bytes on input for newline
45 * and terminating NULL. Note that the practical limit for data
46 * storage in ndbm is (PBLKSIZ - 3 * sizeof (short)). Though this
47 * differs from spec 1170 the common industry implementation does
48 * conform to this slightly lower limit.
51 #define OUTPUTSIZ (PBLKSIZ - 3 * sizeof (short))
52 #define INPUTSIZ (OUTPUTSIZ + 2)
58 static void verify_and_output(const char *key
, char *value
, int lineno
);
63 fprintf(stderr
, "stdhosts [-w] [-n] [in-file]\n");
64 fprintf(stderr
, "\t-w\tprint malformed warning messages.\n");
74 char nadr
[INET6_ADDRSTRLEN
]; /* Contains normalised address */
75 const char *nadrp
; /* Pointer to the normalised address */
77 char *commentp
; /* Pointer to comment character '#' */
80 int lineno
= 0; /* Input line counter */
81 struct in_addr in
; /* Used for normalising the IPv4 address */
82 struct in6_addr in6
; /* Used for normalising the IPv6 address */
83 char *fgetsp
; /* Holds return value for fgets() calls */
84 int endoffile
= 0; /* Set when end of file reached */
86 if (cmd
= strrchr(argv
[0], '/'))
91 while ((c
= getopt(argc
, argv
, "v:wn")) != -1) {
93 case 'w': /* Send warning messages to stderr */
106 fp
= fopen(argv
[optind
], "r");
108 fprintf(stderr
, "%s: can't open %s\n",
116 (fgetsp
= fgets(line
, sizeof (line
), fp
)) != NULL
) {
119 /* Check for comments */
120 if ((commentp
= strchr(line
, '#')) != NULL
) {
121 if ((line
[strlen(line
) - 1] != '\n') &&
122 (strlen(line
) >= (sizeof (line
) - 1))) {
124 * Discard the remainder of the line
125 * until the newline or EOF, then
126 * continue to parse the line. Use
127 * adr[] rather then line[] to
128 * preserve the contents of line[].
130 while ((fgetsp
= fgets(adr
, sizeof (adr
),
132 if (adr
[strlen(adr
) - 1] == '\n')
138 /* Terminate line[] at the comment character */
140 } else if ((line
[strlen(line
) - 1] != '\n') &&
141 (strlen(line
) >= (sizeof (line
) - 1))) {
143 * Catch long lines but not if this is a short
144 * line with no '\n' at the end of the input.
148 "%s: Warning: more than %d "
149 "bytes on line %d, ignored\n",
150 cmd
, sizeof (line
) - 2, lineno
);
152 * Discard the remaining lines until the
155 while ((fgetsp
= fgets(line
, sizeof (line
),
157 if (line
[strlen(line
) - 1] == '\n')
164 if (sscanf(line
, "%s", adr
) != 1) { /* Blank line, ignore */
168 if ((trailer
= strpbrk(line
, " \t")) == NULL
) {
171 "%s: Warning: no host names on line %d, "
172 "ignored\n", cmd
, lineno
);
177 * check for valid addresses
179 * Attempt an ipv4 conversion, this accepts all valid
180 * ipv4 addresses including:
184 * Unfortunately inet_pton() doesn't recognise these.
187 in
.s_addr
= inet_addr(adr
);
188 if (-1 != (int)in
.s_addr
) {
190 * It's safe not to check return of NULL as
191 * nadrp is checked for validity later.
193 nadrp
= inet_ntop(AF_INET
, &in
, nadr
, sizeof (nadr
));
195 nadrp
= NULL
; /* Not a valid IPv4 address */
199 if (inet_pton(AF_INET6
, adr
, &in6
) == 1) {
200 nadrp
= inet_ntop(AF_INET6
, &in6
,
201 nadr
, sizeof (nadr
));
203 if (nadrp
== NULL
) { /* Invalid IPv6 too */
206 "%s: Warning: malformed"
208 " line %d, ignored\n",
212 continue; /* Ignore valid IPv6 */
216 verify_and_output(nadrp
, trailer
, lineno
);
226 * Builds and verifies the output key and value string
228 * It makes sure these rules are followed:
229 * key + separator + value <= OUTPUTSIZ (for ndbm)
230 * names <= MAXALIASES + 1, ie one canonical name + MAXALIASES aliases
231 * It will also ignore everything after a '#' comment character
234 verify_and_output(const char *key
, char *value
, int lineno
)
236 char *p
; /* General char pointer */
237 char *endp
; /* Points to the NULL at the end */
238 char *namep
; /* First character of a name */
239 char tmpbuf
[OUTPUTSIZ
+1]; /* Buffer before writing out */
240 char *tmpbufp
= tmpbuf
; /* Current point in output string */
241 int n
= 0; /* Length of output */
242 int names
= 0; /* Number of names found */
243 int namelen
; /* Length of the name */
245 if (key
) { /* Just in case key is NULL */
250 "%s: address too long on "
251 "line %d, line discarded\n",
255 memcpy(tmpbufp
, key
, n
+1); /* Plus the '\0' */
259 if (value
) { /* Just in case value is NULL */
261 if ((endp
= strchr(value
, '#')) == 0) /* Ignore # comments */
262 endp
= p
+ strlen(p
); /* Or endp = EOL */
265 * Skip white space. Type conversion is
266 * necessary to avoid unfortunate effects of
267 * 8-bit characters appearing negative.
269 while ((p
< endp
) && isspace((unsigned char)*p
))
272 if (p
== endp
) /* End of the string */
276 if (names
> (MAXALIASES
+1)) { /* cname + MAXALIASES */
279 "%s: Warning: too many "
280 "host names on line %d, "
287 while ((p
< endp
) && !isspace((unsigned char)*p
))
291 n
+= namelen
+ 1; /* single white space + name */
292 *p
= '\0'; /* Terminate the name string */
296 "%s: Warning: %d byte ndbm limit "
297 "reached on line %d, truncating\n",
298 cmd
, OUTPUTSIZ
, lineno
);
302 if (names
== 1) /* First space is a '\t' */
307 memcpy(tmpbufp
, namep
, namelen
+1); /* Plus the '\0' */
311 p
++; /* Skip the added NULL */
317 fputs(tmpbuf
, stdout
);
322 "%s: Warning: no host names on line %d, "
323 "ignored\n", cmd
, lineno
);