7 /* network name validation
9 /* #include <valid_hostname.h>
11 /* int valid_hostname(name, gripe)
15 /* int valid_hostaddr(addr, gripe)
19 /* int valid_ipv4_hostaddr(addr, gripe)
23 /* int valid_ipv6_hostaddr(addr, gripe)
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
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.
48 /* valid_hostmumble() does not guarantee that string lengths
49 /* fit the buffer sizes defined in myaddrinfo(3h).
51 /* All functions return zero if they disagree with the input.
53 /* RFC 952, RFC 1123, RFC 1035, RFC 2373.
57 /* The Secure Mailer license must be distributed with this software.
60 /* IBM T.J. Watson Research
62 /* Yorktown Heights, NY 10598, USA
71 /* Utility library. */
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";
90 * Trivial cases first.
94 msg_warn("%s: empty hostname", myname
);
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)
106 if (label_length
> VALID_LABEL_LEN
) {
108 msg_warn("%s: hostname label too long: %.100s", myname
, name
);
113 } else if (ch
== '.') {
114 if (label_length
== 0 || cp
[1] == 0) {
116 msg_warn("%s: misplaced delimiter: %.100s", myname
, name
);
120 } else if (ch
== '-') {
122 if (label_length
== 1 || cp
[1] == 0 || cp
[1] == '.') {
124 msg_warn("%s: misplaced hyphen: %.100s", myname
, name
);
128 #ifdef SLOPPY_VALID_HOSTNAME
129 else if (ch
== ':' && valid_ipv6_hostaddr(name
, DONT_GRIPE
)) {
136 msg_warn("%s: invalid character %d(decimal): %.100s",
142 if (non_numeric
== 0) {
144 msg_warn("%s: numeric hostname: %.100s", myname
, name
);
145 #ifndef SLOPPY_VALID_HOSTNAME
149 if (cp
- name
> VALID_HOSTNAME_LEN
) {
151 msg_warn("%s: bad length %d for %.100s...",
152 myname
, (int) (cp
- name
), name
);
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.
169 msg_warn("%s: empty address", myname
);
174 * Protocol-dependent processing next.
176 if (strchr(addr
, ':') != 0)
177 return (valid_ipv6_hostaddr(addr
, gripe
));
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
)
187 const char *myname
= "valid_ipv4_hostaddr";
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
++) {
209 byte_val
+= ch
- '0';
210 if (byte_val
> 255) {
212 msg_warn("%s: invalid octet value: %.100s", myname
, addr
);
215 } else if (ch
== '.') {
216 if (in_byte
== 0 || cp
[1] == 0) {
218 msg_warn("%s: misplaced dot: %.100s", myname
, addr
);
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.")]) {
224 msg_warn("%s: bad initial octet value: %.100s", myname
, addr
);
230 msg_warn("%s: invalid character %d(decimal): %.100s",
236 if (byte_count
!= BYTES_NEEDED
) {
238 msg_warn("%s: invalid octet count: %.100s", myname
, addr
);
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";
251 unsigned char *cp
= (unsigned char *) addr
;
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.
270 /* Terminate the loop. */
273 msg_warn("%s: too few `:' in IPv6 address: %.100s",
276 } else if (len
== 0 && null_field
!= field
- 1) {
278 msg_warn("%s: bad null last field in IPv6 address: %.100s",
284 /* Terminate the loop. */
285 if (field
< 2 || field
> 6) {
287 msg_warn("%s: malformed IPv4-in-IPv6 address: %.100s",
291 /* NOT: valid_hostaddr(). Avoid recursion. */
292 return (valid_ipv4_hostaddr((char *) cp
- len
, gripe
));
294 /* Advance by exactly 1 character position or terminate. */
295 if (field
== 0 && len
== 0 && ISALNUM(cp
[1])) {
297 msg_warn("%s: bad null first field in IPv6 address: %.100s",
304 msg_warn("%s: too many `:' in IPv6 address: %.100s",
311 if (null_field
> 0) {
313 msg_warn("%s: too many `::' in IPv6 address: %.100s",
321 /* Advance by at least 1 character position or terminate. */
322 len
= strspn((char *) cp
, "0123456789abcdefABCDEF");
323 if (len
/* - strspn((char *) cp, "0") */ > 4) {
325 msg_warn("%s: malformed IPv6 address: %.100s",
331 msg_warn("%s: invalid character %d(decimal) in IPv6 address: %.100s",
344 * Test program - reads hostnames from stdin, reports invalid hostnames to
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
);
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
);