Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / valid_hostname.c
blob4ea3a6606f31b860a462b45fffea03c1d70ee8cb
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* valid_hostname 3
6 /* SUMMARY
7 /* network name validation
8 /* SYNOPSIS
9 /* #include <valid_hostname.h>
11 /* int valid_hostname(name, gripe)
12 /* const char *name;
13 /* int gripe;
15 /* int valid_hostaddr(addr, gripe)
16 /* const char *addr;
17 /* int gripe;
19 /* int valid_ipv4_hostaddr(addr, gripe)
20 /* const char *addr;
21 /* int gripe;
23 /* int valid_ipv6_hostaddr(addr, gripe)
24 /* const char *addr;
25 /* int gripe;
26 /* DESCRIPTION
27 /* valid_hostname() scrutinizes a hostname: the name should
28 /* be no longer than VALID_HOSTNAME_LEN characters, should
29 /* contain only letters, digits, dots and hyphens, no adjacent
30 /* dots and hyphens, no leading or trailing dots or hyphens,
31 /* no labels longer than VALID_LABEL_LEN characters, and it
32 /* should not be all numeric.
34 /* valid_hostaddr() requires that the input is a valid string
35 /* representation of an IPv4 or IPv6 network address as
36 /* described next.
38 /* valid_ipv4_hostaddr() and valid_ipv6_hostaddr() implement
39 /* protocol-specific address syntax checks. A valid IPv4
40 /* address is in dotted-quad decimal form. A valid IPv6 address
41 /* has 16-bit hexadecimal fields separated by ":", and does not
42 /* include the RFC 2821 style "IPv6:" prefix.
44 /* These routines operate silently unless the gripe parameter
45 /* specifies a non-zero value. The macros DO_GRIPE and DONT_GRIPE
46 /* provide suitable constants.
47 /* BUGS
48 /* valid_hostmumble() does not guarantee that string lengths
49 /* fit the buffer sizes defined in myaddrinfo(3h).
50 /* DIAGNOSTICS
51 /* All functions return zero if they disagree with the input.
52 /* SEE ALSO
53 /* RFC 952, RFC 1123, RFC 1035, RFC 2373.
54 /* LICENSE
55 /* .ad
56 /* .fi
57 /* The Secure Mailer license must be distributed with this software.
58 /* AUTHOR(S)
59 /* Wietse Venema
60 /* IBM T.J. Watson Research
61 /* P.O. Box 704
62 /* Yorktown Heights, NY 10598, USA
63 /*--*/
65 /* System library. */
67 #include <sys_defs.h>
68 #include <string.h>
69 #include <ctype.h>
71 /* Utility library. */
73 #include "msg.h"
74 #include "mymalloc.h"
75 #include "stringops.h"
76 #include "valid_hostname.h"
78 /* valid_hostname - screen out bad hostnames */
80 int valid_hostname(const char *name, int gripe)
82 const char *myname = "valid_hostname";
83 const char *cp;
84 int label_length = 0;
85 int label_count = 0;
86 int non_numeric = 0;
87 int ch;
90 * Trivial cases first.
92 if (*name == 0) {
93 if (gripe)
94 msg_warn("%s: empty hostname", myname);
95 return (0);
99 * Find bad characters or label lengths. Find adjacent delimiters.
101 for (cp = name; (ch = *(unsigned char *) cp) != 0; cp++) {
102 if (ISALNUM(ch) || ch == '_') { /* grr.. */
103 if (label_length == 0)
104 label_count++;
105 label_length++;
106 if (label_length > VALID_LABEL_LEN) {
107 if (gripe)
108 msg_warn("%s: hostname label too long: %.100s", myname, name);
109 return (0);
111 if (!ISDIGIT(ch))
112 non_numeric = 1;
113 } else if (ch == '.') {
114 if (label_length == 0 || cp[1] == 0) {
115 if (gripe)
116 msg_warn("%s: misplaced delimiter: %.100s", myname, name);
117 return (0);
119 label_length = 0;
120 } else if (ch == '-') {
121 label_length++;
122 if (label_length == 1 || cp[1] == 0 || cp[1] == '.') {
123 if (gripe)
124 msg_warn("%s: misplaced hyphen: %.100s", myname, name);
125 return (0);
128 #ifdef SLOPPY_VALID_HOSTNAME
129 else if (ch == ':' && valid_ipv6_hostaddr(name, DONT_GRIPE)) {
130 non_numeric = 0;
131 break;
133 #endif
134 else {
135 if (gripe)
136 msg_warn("%s: invalid character %d(decimal): %.100s",
137 myname, ch, name);
138 return (0);
142 if (non_numeric == 0) {
143 if (gripe)
144 msg_warn("%s: numeric hostname: %.100s", myname, name);
145 #ifndef SLOPPY_VALID_HOSTNAME
146 return (0);
147 #endif
149 if (cp - name > VALID_HOSTNAME_LEN) {
150 if (gripe)
151 msg_warn("%s: bad length %d for %.100s...",
152 myname, (int) (cp - name), name);
153 return (0);
155 return (1);
158 /* valid_hostaddr - verify numerical address syntax */
160 int valid_hostaddr(const char *addr, int gripe)
162 const char *myname = "valid_hostaddr";
165 * Trivial cases first.
167 if (*addr == 0) {
168 if (gripe)
169 msg_warn("%s: empty address", myname);
170 return (0);
174 * Protocol-dependent processing next.
176 if (strchr(addr, ':') != 0)
177 return (valid_ipv6_hostaddr(addr, gripe));
178 else
179 return (valid_ipv4_hostaddr(addr, gripe));
182 /* valid_ipv4_hostaddr - test dotted quad string for correctness */
184 int valid_ipv4_hostaddr(const char *addr, int gripe)
186 const char *cp;
187 const char *myname = "valid_ipv4_hostaddr";
188 int in_byte = 0;
189 int byte_count = 0;
190 int byte_val = 0;
191 int ch;
193 #define BYTES_NEEDED 4
196 * Scary code to avoid sscanf() overflow nasties.
198 * This routine is called by valid_ipv6_hostaddr(). It must not call that
199 * routine, to avoid deadly recursion.
201 for (cp = addr; (ch = *(unsigned const char *) cp) != 0; cp++) {
202 if (ISDIGIT(ch)) {
203 if (in_byte == 0) {
204 in_byte = 1;
205 byte_val = 0;
206 byte_count++;
208 byte_val *= 10;
209 byte_val += ch - '0';
210 if (byte_val > 255) {
211 if (gripe)
212 msg_warn("%s: invalid octet value: %.100s", myname, addr);
213 return (0);
215 } else if (ch == '.') {
216 if (in_byte == 0 || cp[1] == 0) {
217 if (gripe)
218 msg_warn("%s: misplaced dot: %.100s", myname, addr);
219 return (0);
221 /* XXX Allow 0.0.0.0 but not 0.1.2.3 */
222 if (byte_count == 1 && byte_val == 0 && addr[strspn(addr, "0.")]) {
223 if (gripe)
224 msg_warn("%s: bad initial octet value: %.100s", myname, addr);
225 return (0);
227 in_byte = 0;
228 } else {
229 if (gripe)
230 msg_warn("%s: invalid character %d(decimal): %.100s",
231 myname, ch, addr);
232 return (0);
236 if (byte_count != BYTES_NEEDED) {
237 if (gripe)
238 msg_warn("%s: invalid octet count: %.100s", myname, addr);
239 return (0);
241 return (1);
244 /* valid_ipv6_hostaddr - validate IPv6 address syntax */
246 int valid_ipv6_hostaddr(const char *addr, int gripe)
248 const char *myname = "valid_ipv6_hostaddr";
249 int null_field = 0;
250 int field = 0;
251 unsigned char *cp = (unsigned char *) addr;
252 int len = 0;
255 * FIX 200501 The IPv6 patch validated syntax with getaddrinfo(), but I
256 * am not confident that everyone's system library routines are robust
257 * enough, like buffer overflow free. Remember, the valid_hostmumble()
258 * routines are meant to protect Postfix against malformed information in
259 * data received from the network.
261 * We require eight-field hex addresses of the form 0:1:2:3:4:5:6:7,
262 * 0:1:2:3:4:5:6a.6b.7c.7d, or some :: compressed version of the same.
264 * Note: the character position is advanced inside the loop. I have added
265 * comments to show why we can't get stuck.
267 for (;;) {
268 switch (*cp) {
269 case 0:
270 /* Terminate the loop. */
271 if (field < 2) {
272 if (gripe)
273 msg_warn("%s: too few `:' in IPv6 address: %.100s",
274 myname, addr);
275 return (0);
276 } else if (len == 0 && null_field != field - 1) {
277 if (gripe)
278 msg_warn("%s: bad null last field in IPv6 address: %.100s",
279 myname, addr);
280 return (0);
281 } else
282 return (1);
283 case '.':
284 /* Terminate the loop. */
285 if (field < 2 || field > 6) {
286 if (gripe)
287 msg_warn("%s: malformed IPv4-in-IPv6 address: %.100s",
288 myname, addr);
289 return (0);
290 } else
291 /* NOT: valid_hostaddr(). Avoid recursion. */
292 return (valid_ipv4_hostaddr((char *) cp - len, gripe));
293 case ':':
294 /* Advance by exactly 1 character position or terminate. */
295 if (field == 0 && len == 0 && ISALNUM(cp[1])) {
296 if (gripe)
297 msg_warn("%s: bad null first field in IPv6 address: %.100s",
298 myname, addr);
299 return (0);
301 field++;
302 if (field > 7) {
303 if (gripe)
304 msg_warn("%s: too many `:' in IPv6 address: %.100s",
305 myname, addr);
306 return (0);
308 cp++;
309 len = 0;
310 if (*cp == ':') {
311 if (null_field > 0) {
312 if (gripe)
313 msg_warn("%s: too many `::' in IPv6 address: %.100s",
314 myname, addr);
315 return (0);
317 null_field = field;
319 break;
320 default:
321 /* Advance by at least 1 character position or terminate. */
322 len = strspn((char *) cp, "0123456789abcdefABCDEF");
323 if (len /* - strspn((char *) cp, "0") */ > 4) {
324 if (gripe)
325 msg_warn("%s: malformed IPv6 address: %.100s",
326 myname, addr);
327 return (0);
329 if (len <= 0) {
330 if (gripe)
331 msg_warn("%s: invalid character %d(decimal) in IPv6 address: %.100s",
332 myname, *cp, addr);
333 return (0);
335 cp += len;
336 break;
341 #ifdef TEST
344 * Test program - reads hostnames from stdin, reports invalid hostnames to
345 * stderr.
347 #include <stdlib.h>
349 #include "vstring.h"
350 #include "vstream.h"
351 #include "vstring_vstream.h"
352 #include "msg_vstream.h"
354 int main(int unused_argc, char **argv)
356 VSTRING *buffer = vstring_alloc(1);
358 msg_vstream_init(argv[0], VSTREAM_ERR);
359 msg_verbose = 1;
361 while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
362 msg_info("testing: \"%s\"", vstring_str(buffer));
363 valid_hostname(vstring_str(buffer), DO_GRIPE);
364 valid_hostaddr(vstring_str(buffer), DO_GRIPE);
366 exit(0);
369 #endif