Postfix3.3.1
valid_hostname.c
[詳解]
1 /*++
2 /* NAME
3 /* valid_hostname 3
4 /* SUMMARY
5 /* network name validation
6 /* SYNOPSIS
7 /* #include <valid_hostname.h>
8 /*
9 /* int valid_hostname(name, gripe)
10 /* const char *name;
11 /* int gripe;
12 /*
13 /* int valid_hostaddr(addr, gripe)
14 /* const char *addr;
15 /* int gripe;
16 /*
17 /* int valid_ipv4_hostaddr(addr, gripe)
18 /* const char *addr;
19 /* int gripe;
20 /*
21 /* int valid_ipv6_hostaddr(addr, gripe)
22 /* const char *addr;
23 /* int gripe;
24 /*
25 /* int valid_hostport(port, gripe)
26 /* const char *port;
27 /* int gripe;
28 /* DESCRIPTION
29 /* valid_hostname() scrutinizes a hostname: the name should
30 /* be no longer than VALID_HOSTNAME_LEN characters, should
31 /* contain only letters, digits, dots and hyphens, no adjacent
32 /* dots, no leading or trailing dots or hyphens, no labels
33 /* longer than VALID_LABEL_LEN characters, and it should not
34 /* be all numeric.
35 /*
36 /* valid_hostaddr() requires that the input is a valid string
37 /* representation of an IPv4 or IPv6 network address as
38 /* described next.
39 /*
40 /* valid_ipv4_hostaddr() and valid_ipv6_hostaddr() implement
41 /* protocol-specific address syntax checks. A valid IPv4
42 /* address is in dotted-quad decimal form. A valid IPv6 address
43 /* has 16-bit hexadecimal fields separated by ":", and does not
44 /* include the RFC 2821 style "IPv6:" prefix.
45 /*
46 /* These routines operate silently unless the gripe parameter
47 /* specifies a non-zero value. The macros DO_GRIPE and DONT_GRIPE
48 /* provide suitable constants.
49 /*
50 /* valid_hostport() requires that the input is a valid string
51 /* representation of a TCP or UDP port number.
52 /* BUGS
53 /* valid_hostmumble() does not guarantee that string lengths
54 /* fit the buffer sizes defined in myaddrinfo(3h).
55 /* DIAGNOSTICS
56 /* All functions return zero if they disagree with the input.
57 /* SEE ALSO
58 /* RFC 952, RFC 1123, RFC 1035, RFC 2373.
59 /* LICENSE
60 /* .ad
61 /* .fi
62 /* The Secure Mailer license must be distributed with this software.
63 /* AUTHOR(S)
64 /* Wietse Venema
65 /* IBM T.J. Watson Research
66 /* P.O. Box 704
67 /* Yorktown Heights, NY 10598, USA
68 /*--*/
69 
70 /* System library. */
71 
72 #include <sys_defs.h>
73 #include <string.h>
74 #include <ctype.h>
75 #include <stdlib.h>
76 
77 /* Utility library. */
78 
79 #include "msg.h"
80 #include "mymalloc.h"
81 #include "stringops.h"
82 #include "valid_hostname.h"
83 
84 /* valid_hostname - screen out bad hostnames */
85 
86 int valid_hostname(const char *name, int gripe)
87 {
88  const char *myname = "valid_hostname";
89  const char *cp;
90  int label_length = 0;
91  int label_count = 0;
92  int non_numeric = 0;
93  int ch;
94 
95  /*
96  * Trivial cases first.
97  */
98  if (*name == 0) {
99  if (gripe)
100  msg_warn("%s: empty hostname", myname);
101  return (0);
102  }
103 
104  /*
105  * Find bad characters or label lengths. Find adjacent delimiters.
106  */
107  for (cp = name; (ch = *(unsigned char *) cp) != 0; cp++) {
108  if (ISALNUM(ch) || ch == '_') { /* grr.. */
109  if (label_length == 0)
110  label_count++;
111  label_length++;
112  if (label_length > VALID_LABEL_LEN) {
113  if (gripe)
114  msg_warn("%s: hostname label too long: %.100s", myname, name);
115  return (0);
116  }
117  if (!ISDIGIT(ch))
118  non_numeric = 1;
119  } else if (ch == '.') {
120  if (label_length == 0 || cp[1] == 0) {
121  if (gripe)
122  msg_warn("%s: misplaced delimiter: %.100s", myname, name);
123  return (0);
124  }
125  label_length = 0;
126  } else if (ch == '-') {
127  non_numeric = 1;
128  label_length++;
129  if (label_length == 1 || cp[1] == 0 || cp[1] == '.') {
130  if (gripe)
131  msg_warn("%s: misplaced hyphen: %.100s", myname, name);
132  return (0);
133  }
134  }
135 #ifdef SLOPPY_VALID_HOSTNAME
136  else if (ch == ':' && valid_ipv6_hostaddr(name, DONT_GRIPE)) {
137  non_numeric = 0;
138  break;
139  }
140 #endif
141  else {
142  if (gripe)
143  msg_warn("%s: invalid character %d(decimal): %.100s",
144  myname, ch, name);
145  return (0);
146  }
147  }
148 
149  if (non_numeric == 0) {
150  if (gripe)
151  msg_warn("%s: numeric hostname: %.100s", myname, name);
152 #ifndef SLOPPY_VALID_HOSTNAME
153  return (0);
154 #endif
155  }
156  if (cp - name > VALID_HOSTNAME_LEN) {
157  if (gripe)
158  msg_warn("%s: bad length %d for %.100s...",
159  myname, (int) (cp - name), name);
160  return (0);
161  }
162  return (1);
163 }
164 
165 /* valid_hostaddr - verify numerical address syntax */
166 
167 int valid_hostaddr(const char *addr, int gripe)
168 {
169  const char *myname = "valid_hostaddr";
170 
171  /*
172  * Trivial cases first.
173  */
174  if (*addr == 0) {
175  if (gripe)
176  msg_warn("%s: empty address", myname);
177  return (0);
178  }
179 
180  /*
181  * Protocol-dependent processing next.
182  */
183  if (strchr(addr, ':') != 0)
184  return (valid_ipv6_hostaddr(addr, gripe));
185  else
186  return (valid_ipv4_hostaddr(addr, gripe));
187 }
188 
189 /* valid_ipv4_hostaddr - test dotted quad string for correctness */
190 
191 int valid_ipv4_hostaddr(const char *addr, int gripe)
192 {
193  const char *cp;
194  const char *myname = "valid_ipv4_hostaddr";
195  int in_byte = 0;
196  int byte_count = 0;
197  int byte_val = 0;
198  int ch;
199 
200 #define BYTES_NEEDED 4
201 
202  /*
203  * Scary code to avoid sscanf() overflow nasties.
204  *
205  * This routine is called by valid_ipv6_hostaddr(). It must not call that
206  * routine, to avoid deadly recursion.
207  */
208  for (cp = addr; (ch = *(unsigned const char *) cp) != 0; cp++) {
209  if (ISDIGIT(ch)) {
210  if (in_byte == 0) {
211  in_byte = 1;
212  byte_val = 0;
213  byte_count++;
214  }
215  byte_val *= 10;
216  byte_val += ch - '0';
217  if (byte_val > 255) {
218  if (gripe)
219  msg_warn("%s: invalid octet value: %.100s", myname, addr);
220  return (0);
221  }
222  } else if (ch == '.') {
223  if (in_byte == 0 || cp[1] == 0) {
224  if (gripe)
225  msg_warn("%s: misplaced dot: %.100s", myname, addr);
226  return (0);
227  }
228  /* XXX Allow 0.0.0.0 but not 0.1.2.3 */
229  if (byte_count == 1 && byte_val == 0 && addr[strspn(addr, "0.")]) {
230  if (gripe)
231  msg_warn("%s: bad initial octet value: %.100s", myname, addr);
232  return (0);
233  }
234  in_byte = 0;
235  } else {
236  if (gripe)
237  msg_warn("%s: invalid character %d(decimal): %.100s",
238  myname, ch, addr);
239  return (0);
240  }
241  }
242 
243  if (byte_count != BYTES_NEEDED) {
244  if (gripe)
245  msg_warn("%s: invalid octet count: %.100s", myname, addr);
246  return (0);
247  }
248  return (1);
249 }
250 
251 /* valid_ipv6_hostaddr - validate IPv6 address syntax */
252 
253 int valid_ipv6_hostaddr(const char *addr, int gripe)
254 {
255  const char *myname = "valid_ipv6_hostaddr";
256  int null_field = 0;
257  int field = 0;
258  unsigned char *cp = (unsigned char *) addr;
259  int len = 0;
260 
261  /*
262  * FIX 200501 The IPv6 patch validated syntax with getaddrinfo(), but I
263  * am not confident that everyone's system library routines are robust
264  * enough, like buffer overflow free. Remember, the valid_hostmumble()
265  * routines are meant to protect Postfix against malformed information in
266  * data received from the network.
267  *
268  * We require eight-field hex addresses of the form 0:1:2:3:4:5:6:7,
269  * 0:1:2:3:4:5:6a.6b.7c.7d, or some :: compressed version of the same.
270  *
271  * Note: the character position is advanced inside the loop. I have added
272  * comments to show why we can't get stuck.
273  */
274  for (;;) {
275  switch (*cp) {
276  case 0:
277  /* Terminate the loop. */
278  if (field < 2) {
279  if (gripe)
280  msg_warn("%s: too few `:' in IPv6 address: %.100s",
281  myname, addr);
282  return (0);
283  } else if (len == 0 && null_field != field - 1) {
284  if (gripe)
285  msg_warn("%s: bad null last field in IPv6 address: %.100s",
286  myname, addr);
287  return (0);
288  } else
289  return (1);
290  case '.':
291  /* Terminate the loop. */
292  if (field < 2 || field > 6) {
293  if (gripe)
294  msg_warn("%s: malformed IPv4-in-IPv6 address: %.100s",
295  myname, addr);
296  return (0);
297  } else
298  /* NOT: valid_hostaddr(). Avoid recursion. */
299  return (valid_ipv4_hostaddr((char *) cp - len, gripe));
300  case ':':
301  /* Advance by exactly 1 character position or terminate. */
302  if (field == 0 && len == 0 && ISALNUM(cp[1])) {
303  if (gripe)
304  msg_warn("%s: bad null first field in IPv6 address: %.100s",
305  myname, addr);
306  return (0);
307  }
308  field++;
309  if (field > 7) {
310  if (gripe)
311  msg_warn("%s: too many `:' in IPv6 address: %.100s",
312  myname, addr);
313  return (0);
314  }
315  cp++;
316  len = 0;
317  if (*cp == ':') {
318  if (null_field > 0) {
319  if (gripe)
320  msg_warn("%s: too many `::' in IPv6 address: %.100s",
321  myname, addr);
322  return (0);
323  }
324  null_field = field;
325  }
326  break;
327  default:
328  /* Advance by at least 1 character position or terminate. */
329  len = strspn((char *) cp, "0123456789abcdefABCDEF");
330  if (len /* - strspn((char *) cp, "0") */ > 4) {
331  if (gripe)
332  msg_warn("%s: malformed IPv6 address: %.100s",
333  myname, addr);
334  return (0);
335  }
336  if (len <= 0) {
337  if (gripe)
338  msg_warn("%s: invalid character %d(decimal) in IPv6 address: %.100s",
339  myname, *cp, addr);
340  return (0);
341  }
342  cp += len;
343  break;
344  }
345  }
346 }
347 
348 /* valid_hostport - validate numeric port */
349 
350 int valid_hostport(const char *str, int gripe)
351 {
352  const char *myname = "valid_hostport";
353  int port;
354 
355  if (str[0] == '0' && str[1] != 0) {
356  if (gripe)
357  msg_warn("%s: leading zero in port number: %.100s", myname, str);
358  return (0);
359  }
360  if (alldig(str) == 0) {
361  if (gripe)
362  msg_warn("%s: non-numeric port number: %.100s", myname, str);
363  return (0);
364  }
365  if (strlen(str) > strlen("65535")
366  || (port = atoi(str)) > 65535 || port < 0) {
367  if (gripe)
368  msg_warn("%s: out-of-range port number: %.100s", myname, str);
369  return (0);
370  }
371  return (1);
372 }
373 
374 #ifdef TEST
375 
376  /*
377  * Test program - reads hostnames from stdin, reports invalid hostnames to
378  * stderr.
379  */
380 #include <stdlib.h>
381 
382 #include "vstring.h"
383 #include "vstream.h"
384 #include "vstring_vstream.h"
385 #include "msg_vstream.h"
386 
387 int main(int unused_argc, char **argv)
388 {
389  VSTRING *buffer = vstring_alloc(1);
390 
391  msg_vstream_init(argv[0], VSTREAM_ERR);
392  msg_verbose = 1;
393 
394  while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
395  msg_info("testing: \"%s\"", vstring_str(buffer));
398  }
399  exit(0);
400 }
401 
402 #endif
int msg_verbose
Definition: msg.c:177
int valid_hostaddr(const char *addr, int gripe)
#define vstring_fgets_nonl(s, p)
int valid_ipv6_hostaddr(const char *addr, int gripe)
#define VALID_HOSTNAME_LEN
#define vstring_str(vp)
Definition: vstring.h:71
int main(int argc, char **argv)
Definition: anvil.c:1010
int valid_hostport(const char *str, int gripe)
#define VSTREAM_IN
Definition: vstream.h:66
int alldig(const char *string)
Definition: alldig.c:38
int valid_ipv4_hostaddr(const char *addr, int gripe)
#define ISALNUM(c)
Definition: sys_defs.h:1745
#define BYTES_NEEDED
int valid_hostname(const char *name, int gripe)
#define ISDIGIT(c)
Definition: sys_defs.h:1748
#define VALID_LABEL_LEN
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define DO_GRIPE
Definition: haproxy_srvr.h:30
void msg_vstream_init(const char *name, VSTREAM *vp)
Definition: msg_vstream.c:77
#define VSTREAM_ERR
Definition: vstream.h:68
#define DONT_GRIPE
Definition: haproxy_srvr.h:31
void msg_info(const char *fmt,...)
Definition: msg.c:199