Current File : //root/postfix-3.2.0/src/util/valid_hostname.c |
/*++
/* NAME
/* valid_hostname 3
/* SUMMARY
/* network name validation
/* SYNOPSIS
/* #include <valid_hostname.h>
/*
/* int valid_hostname(name, gripe)
/* const char *name;
/* int gripe;
/*
/* int valid_hostaddr(addr, gripe)
/* const char *addr;
/* int gripe;
/*
/* int valid_ipv4_hostaddr(addr, gripe)
/* const char *addr;
/* int gripe;
/*
/* int valid_ipv6_hostaddr(addr, gripe)
/* const char *addr;
/* int gripe;
/*
/* int valid_hostport(port, gripe)
/* const char *port;
/* int gripe;
/* DESCRIPTION
/* valid_hostname() scrutinizes a hostname: the name should
/* be no longer than VALID_HOSTNAME_LEN characters, should
/* contain only letters, digits, dots and hyphens, no adjacent
/* dots, no leading or trailing dots or hyphens, no labels
/* longer than VALID_LABEL_LEN characters, and it should not
/* be all numeric.
/*
/* valid_hostaddr() requires that the input is a valid string
/* representation of an IPv4 or IPv6 network address as
/* described next.
/*
/* valid_ipv4_hostaddr() and valid_ipv6_hostaddr() implement
/* protocol-specific address syntax checks. A valid IPv4
/* address is in dotted-quad decimal form. A valid IPv6 address
/* has 16-bit hexadecimal fields separated by ":", and does not
/* include the RFC 2821 style "IPv6:" prefix.
/*
/* These routines operate silently unless the gripe parameter
/* specifies a non-zero value. The macros DO_GRIPE and DONT_GRIPE
/* provide suitable constants.
/*
/* valid_hostport() requires that the input is a valid string
/* representation of a TCP or UDP port number.
/* BUGS
/* valid_hostmumble() does not guarantee that string lengths
/* fit the buffer sizes defined in myaddrinfo(3h).
/* DIAGNOSTICS
/* All functions return zero if they disagree with the input.
/* SEE ALSO
/* RFC 952, RFC 1123, RFC 1035, RFC 2373.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
/* Utility library. */
#include "msg.h"
#include "mymalloc.h"
#include "stringops.h"
#include "valid_hostname.h"
/* valid_hostname - screen out bad hostnames */
int valid_hostname(const char *name, int gripe)
{
const char *myname = "valid_hostname";
const char *cp;
int label_length = 0;
int label_count = 0;
int non_numeric = 0;
int ch;
/*
* Trivial cases first.
*/
if (*name == 0) {
if (gripe)
msg_warn("%s: empty hostname", myname);
return (0);
}
/*
* Find bad characters or label lengths. Find adjacent delimiters.
*/
for (cp = name; (ch = *(unsigned char *) cp) != 0; cp++) {
if (ISALNUM(ch) || ch == '_') { /* grr.. */
if (label_length == 0)
label_count++;
label_length++;
if (label_length > VALID_LABEL_LEN) {
if (gripe)
msg_warn("%s: hostname label too long: %.100s", myname, name);
return (0);
}
if (!ISDIGIT(ch))
non_numeric = 1;
} else if (ch == '.') {
if (label_length == 0 || cp[1] == 0) {
if (gripe)
msg_warn("%s: misplaced delimiter: %.100s", myname, name);
return (0);
}
label_length = 0;
} else if (ch == '-') {
non_numeric = 1;
label_length++;
if (label_length == 1 || cp[1] == 0 || cp[1] == '.') {
if (gripe)
msg_warn("%s: misplaced hyphen: %.100s", myname, name);
return (0);
}
}
#ifdef SLOPPY_VALID_HOSTNAME
else if (ch == ':' && valid_ipv6_hostaddr(name, DONT_GRIPE)) {
non_numeric = 0;
break;
}
#endif
else {
if (gripe)
msg_warn("%s: invalid character %d(decimal): %.100s",
myname, ch, name);
return (0);
}
}
if (non_numeric == 0) {
if (gripe)
msg_warn("%s: numeric hostname: %.100s", myname, name);
#ifndef SLOPPY_VALID_HOSTNAME
return (0);
#endif
}
if (cp - name > VALID_HOSTNAME_LEN) {
if (gripe)
msg_warn("%s: bad length %d for %.100s...",
myname, (int) (cp - name), name);
return (0);
}
return (1);
}
/* valid_hostaddr - verify numerical address syntax */
int valid_hostaddr(const char *addr, int gripe)
{
const char *myname = "valid_hostaddr";
/*
* Trivial cases first.
*/
if (*addr == 0) {
if (gripe)
msg_warn("%s: empty address", myname);
return (0);
}
/*
* Protocol-dependent processing next.
*/
if (strchr(addr, ':') != 0)
return (valid_ipv6_hostaddr(addr, gripe));
else
return (valid_ipv4_hostaddr(addr, gripe));
}
/* valid_ipv4_hostaddr - test dotted quad string for correctness */
int valid_ipv4_hostaddr(const char *addr, int gripe)
{
const char *cp;
const char *myname = "valid_ipv4_hostaddr";
int in_byte = 0;
int byte_count = 0;
int byte_val = 0;
int ch;
#define BYTES_NEEDED 4
/*
* Scary code to avoid sscanf() overflow nasties.
*
* This routine is called by valid_ipv6_hostaddr(). It must not call that
* routine, to avoid deadly recursion.
*/
for (cp = addr; (ch = *(unsigned const char *) cp) != 0; cp++) {
if (ISDIGIT(ch)) {
if (in_byte == 0) {
in_byte = 1;
byte_val = 0;
byte_count++;
}
byte_val *= 10;
byte_val += ch - '0';
if (byte_val > 255) {
if (gripe)
msg_warn("%s: invalid octet value: %.100s", myname, addr);
return (0);
}
} else if (ch == '.') {
if (in_byte == 0 || cp[1] == 0) {
if (gripe)
msg_warn("%s: misplaced dot: %.100s", myname, addr);
return (0);
}
/* XXX Allow 0.0.0.0 but not 0.1.2.3 */
if (byte_count == 1 && byte_val == 0 && addr[strspn(addr, "0.")]) {
if (gripe)
msg_warn("%s: bad initial octet value: %.100s", myname, addr);
return (0);
}
in_byte = 0;
} else {
if (gripe)
msg_warn("%s: invalid character %d(decimal): %.100s",
myname, ch, addr);
return (0);
}
}
if (byte_count != BYTES_NEEDED) {
if (gripe)
msg_warn("%s: invalid octet count: %.100s", myname, addr);
return (0);
}
return (1);
}
/* valid_ipv6_hostaddr - validate IPv6 address syntax */
int valid_ipv6_hostaddr(const char *addr, int gripe)
{
const char *myname = "valid_ipv6_hostaddr";
int null_field = 0;
int field = 0;
unsigned char *cp = (unsigned char *) addr;
int len = 0;
/*
* FIX 200501 The IPv6 patch validated syntax with getaddrinfo(), but I
* am not confident that everyone's system library routines are robust
* enough, like buffer overflow free. Remember, the valid_hostmumble()
* routines are meant to protect Postfix against malformed information in
* data received from the network.
*
* We require eight-field hex addresses of the form 0:1:2:3:4:5:6:7,
* 0:1:2:3:4:5:6a.6b.7c.7d, or some :: compressed version of the same.
*
* Note: the character position is advanced inside the loop. I have added
* comments to show why we can't get stuck.
*/
for (;;) {
switch (*cp) {
case 0:
/* Terminate the loop. */
if (field < 2) {
if (gripe)
msg_warn("%s: too few `:' in IPv6 address: %.100s",
myname, addr);
return (0);
} else if (len == 0 && null_field != field - 1) {
if (gripe)
msg_warn("%s: bad null last field in IPv6 address: %.100s",
myname, addr);
return (0);
} else
return (1);
case '.':
/* Terminate the loop. */
if (field < 2 || field > 6) {
if (gripe)
msg_warn("%s: malformed IPv4-in-IPv6 address: %.100s",
myname, addr);
return (0);
} else
/* NOT: valid_hostaddr(). Avoid recursion. */
return (valid_ipv4_hostaddr((char *) cp - len, gripe));
case ':':
/* Advance by exactly 1 character position or terminate. */
if (field == 0 && len == 0 && ISALNUM(cp[1])) {
if (gripe)
msg_warn("%s: bad null first field in IPv6 address: %.100s",
myname, addr);
return (0);
}
field++;
if (field > 7) {
if (gripe)
msg_warn("%s: too many `:' in IPv6 address: %.100s",
myname, addr);
return (0);
}
cp++;
len = 0;
if (*cp == ':') {
if (null_field > 0) {
if (gripe)
msg_warn("%s: too many `::' in IPv6 address: %.100s",
myname, addr);
return (0);
}
null_field = field;
}
break;
default:
/* Advance by at least 1 character position or terminate. */
len = strspn((char *) cp, "0123456789abcdefABCDEF");
if (len /* - strspn((char *) cp, "0") */ > 4) {
if (gripe)
msg_warn("%s: malformed IPv6 address: %.100s",
myname, addr);
return (0);
}
if (len <= 0) {
if (gripe)
msg_warn("%s: invalid character %d(decimal) in IPv6 address: %.100s",
myname, *cp, addr);
return (0);
}
cp += len;
break;
}
}
}
/* valid_hostport - validate numeric port */
int valid_hostport(const char *str, int gripe)
{
const char *myname = "valid_hostport";
int port;
if (str[0] == '0' && str[1] != 0) {
if (gripe)
msg_warn("%s: leading zero in port number: %.100s", myname, str);
return (0);
}
if (alldig(str) == 0) {
if (gripe)
msg_warn("%s: non-numeric port number: %.100s", myname, str);
return (0);
}
if (strlen(str) > strlen("65535")
|| (port = atoi(str)) > 65535 || port < 0) {
if (gripe)
msg_warn("%s: out-of-range port number: %.100s", myname, str);
return (0);
}
return (1);
}
#ifdef TEST
/*
* Test program - reads hostnames from stdin, reports invalid hostnames to
* stderr.
*/
#include <stdlib.h>
#include "vstring.h"
#include "vstream.h"
#include "vstring_vstream.h"
#include "msg_vstream.h"
int main(int unused_argc, char **argv)
{
VSTRING *buffer = vstring_alloc(1);
msg_vstream_init(argv[0], VSTREAM_ERR);
msg_verbose = 1;
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
msg_info("testing: \"%s\"", vstring_str(buffer));
valid_hostname(vstring_str(buffer), DO_GRIPE);
valid_hostaddr(vstring_str(buffer), DO_GRIPE);
}
exit(0);
}
#endif