diff --git a/cmdparse.c b/cmdparse.c index e2c5115..d44b426 100644 --- a/cmdparse.c +++ b/cmdparse.c @@ -181,6 +181,85 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) /* ================================================== */ +int +CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits) +{ + char *p, *net, *slash; + uint32_t a, b, c; + int bits, len, n; + + p = CPS_SplitWord(line); + + if (strcmp(line, "all") == 0) { + *all = 1; + net = p; + p = CPS_SplitWord(p); + } else { + *all = 0; + net = line; + } + + /* Make sure there are no other arguments */ + if (*p) + return 0; + + /* No specified address or network means all IPv4 and IPv6 addresses */ + if (!*net) { + ip->family = IPADDR_UNSPEC; + *subnet_bits = 0; + return 1; + } + + slash = strchr(net, '/'); + if (slash) { + if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0) + return 0; + *slash = '\0'; + } else { + bits = -1; + } + + if (UTI_StringToIP(net, ip)) { + if (bits >= 0) + *subnet_bits = bits; + else + *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32; + return 1; + } + + /* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal + numbers. This is different than the numbers-and-dots notation accepted + by inet_aton()! */ + + a = b = c = 0; + n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len); + + if (n > 0 && !net[len]) { + if (a > 255 || b > 255 || c > 255) + return 0; + + ip->family = IPADDR_INET4; + ip->addr.in4 = (a << 24) | (b << 16) | (c << 8); + + if (bits >= 0) + *subnet_bits = bits; + else + *subnet_bits = n * 8; + + return 1; + } + + /* The last possibility is a hostname */ + if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) { + *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32; + return 1; + } + + return 0; +} + +/* ================================================== */ + int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance) { diff --git a/cmdparse.h b/cmdparse.h index 199d972..fd1eb43 100644 --- a/cmdparse.h +++ b/cmdparse.h @@ -39,6 +39,9 @@ typedef struct { /* Parse a command to add an NTP server or peer */ extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); +/* Parse a command to allow/deny access */ +extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits); + /* Parse a command to enable local reference */ extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance); diff --git a/conf.c b/conf.c index 93e93fc..06ea1e5 100644 --- a/conf.c +++ b/conf.c @@ -1217,100 +1217,18 @@ parse_ntstrustedcerts(char *line) static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow) { - char *p; - unsigned long a, b, c, d, n; - int all = 0; - AllowDeny *new_node = NULL; - IPAddr ip_addr; + int all, subnet_bits; + AllowDeny *node; + IPAddr ip; - p = line; + if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) + command_parse_error(); - if (!strncmp(p, "all", 3)) { - all = 1; - p = CPS_SplitWord(line); - } - - if (!*p) { - /* Empty line applies to all addresses */ - new_node = (AllowDeny *)ARR_GetNewElement(restrictions); - new_node->allow = allow; - new_node->all = all; - new_node->ip.family = IPADDR_UNSPEC; - new_node->subnet_bits = 0; - } else { - char *slashpos; - slashpos = strchr(p, '/'); - if (slashpos) *slashpos = 0; - - check_number_of_args(p, 1); - n = 0; - if (UTI_StringToIP(p, &ip_addr) || - (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) >= 1) { - new_node = (AllowDeny *)ARR_GetNewElement(restrictions); - new_node->allow = allow; - new_node->all = all; - - if (n == 0) { - new_node->ip = ip_addr; - if (ip_addr.family == IPADDR_INET6) - new_node->subnet_bits = 128; - else - new_node->subnet_bits = 32; - } else { - new_node->ip.family = IPADDR_INET4; - - a &= 0xff; - b &= 0xff; - c &= 0xff; - d &= 0xff; - - switch (n) { - case 1: - new_node->ip.addr.in4 = (a<<24); - new_node->subnet_bits = 8; - break; - case 2: - new_node->ip.addr.in4 = (a<<24) | (b<<16); - new_node->subnet_bits = 16; - break; - case 3: - new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8); - new_node->subnet_bits = 24; - break; - case 4: - new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8) | d; - new_node->subnet_bits = 32; - break; - default: - assert(0); - } - } - - if (slashpos) { - int specified_subnet_bits, n; - n = sscanf(slashpos+1, "%d", &specified_subnet_bits); - if (n == 1) { - new_node->subnet_bits = specified_subnet_bits; - } else { - command_parse_error(); - } - } - - } else { - if (!slashpos && DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) { - new_node = (AllowDeny *)ARR_GetNewElement(restrictions); - new_node->allow = allow; - new_node->all = all; - new_node->ip = ip_addr; - if (ip_addr.family == IPADDR_INET6) - new_node->subnet_bits = 128; - else - new_node->subnet_bits = 32; - } else { - command_parse_error(); - } - } - } + node = ARR_GetNewElement(restrictions); + node->allow = allow; + node->all = all; + node->ip = ip; + node->subnet_bits = subnet_bits; } /* ================================================== */