1 /* $NetBSD: valid_hostname.c,v 1.1.1.1 2009/06/23 10:09:01 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* valid_hostname 3 6 /* SUMMARY 7 /* network name validation 8 /* SYNOPSIS 9 /* #include <valid_hostname.h> 10 /* 11 /* int valid_hostname(name, gripe) 12 /* const char *name; 13 /* int gripe; 14 /* 15 /* int valid_hostaddr(addr, gripe) 16 /* const char *addr; 17 /* int gripe; 18 /* 19 /* int valid_ipv4_hostaddr(addr, gripe) 20 /* const char *addr; 21 /* int gripe; 22 /* 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. 33 /* 34 /* valid_hostaddr() requires that the input is a valid string 35 /* representation of an IPv4 or IPv6 network address as 36 /* described next. 37 /* 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. 43 /* 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 /*--*/ 64 65 /* System library. */ 66 67 #include <sys_defs.h> 68 #include <string.h> 69 #include <ctype.h> 70 71 /* Utility library. */ 72 73 #include "msg.h" 74 #include "mymalloc.h" 75 #include "stringops.h" 76 #include "valid_hostname.h" 77 78 /* valid_hostname - screen out bad hostnames */ 79 80 int valid_hostname(const char *name, int gripe) 81 { 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; 88 89 /* 90 * Trivial cases first. 91 */ 92 if (*name == 0) { 93 if (gripe) 94 msg_warn("%s: empty hostname", myname); 95 return (0); 96 } 97 98 /* 99 * Find bad characters or label lengths. Find adjacent delimiters. 100 */ 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); 110 } 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); 118 } 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); 126 } 127 } 128 #ifdef SLOPPY_VALID_HOSTNAME 129 else if (ch == ':' && valid_ipv6_hostaddr(name, DONT_GRIPE)) { 130 non_numeric = 0; 131 break; 132 } 133 #endif 134 else { 135 if (gripe) 136 msg_warn("%s: invalid character %d(decimal): %.100s", 137 myname, ch, name); 138 return (0); 139 } 140 } 141 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 148 } 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); 154 } 155 return (1); 156 } 157 158 /* valid_hostaddr - verify numerical address syntax */ 159 160 int valid_hostaddr(const char *addr, int gripe) 161 { 162 const char *myname = "valid_hostaddr"; 163 164 /* 165 * Trivial cases first. 166 */ 167 if (*addr == 0) { 168 if (gripe) 169 msg_warn("%s: empty address", myname); 170 return (0); 171 } 172 173 /* 174 * Protocol-dependent processing next. 175 */ 176 if (strchr(addr, ':') != 0) 177 return (valid_ipv6_hostaddr(addr, gripe)); 178 else 179 return (valid_ipv4_hostaddr(addr, gripe)); 180 } 181 182 /* valid_ipv4_hostaddr - test dotted quad string for correctness */ 183 184 int valid_ipv4_hostaddr(const char *addr, int gripe) 185 { 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; 192 193 #define BYTES_NEEDED 4 194 195 /* 196 * Scary code to avoid sscanf() overflow nasties. 197 * 198 * This routine is called by valid_ipv6_hostaddr(). It must not call that 199 * routine, to avoid deadly recursion. 200 */ 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++; 207 } 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); 214 } 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); 220 } 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); 226 } 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); 233 } 234 } 235 236 if (byte_count != BYTES_NEEDED) { 237 if (gripe) 238 msg_warn("%s: invalid octet count: %.100s", myname, addr); 239 return (0); 240 } 241 return (1); 242 } 243 244 /* valid_ipv6_hostaddr - validate IPv6 address syntax */ 245 246 int valid_ipv6_hostaddr(const char *addr, int gripe) 247 { 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; 253 254 /* 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. 260 * 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. 263 * 264 * Note: the character position is advanced inside the loop. I have added 265 * comments to show why we can't get stuck. 266 */ 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); 300 } 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); 307 } 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); 316 } 317 null_field = field; 318 } 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); 328 } 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); 334 } 335 cp += len; 336 break; 337 } 338 } 339 } 340 341 #ifdef TEST 342 343 /* 344 * Test program - reads hostnames from stdin, reports invalid hostnames to 345 * stderr. 346 */ 347 #include <stdlib.h> 348 349 #include "vstring.h" 350 #include "vstream.h" 351 #include "vstring_vstream.h" 352 #include "msg_vstream.h" 353 354 int main(int unused_argc, char **argv) 355 { 356 VSTRING *buffer = vstring_alloc(1); 357 358 msg_vstream_init(argv[0], VSTREAM_ERR); 359 msg_verbose = 1; 360 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); 365 } 366 exit(0); 367 } 368 369 #endif 370