pr_netacl_t *pr_netacl_create(pool *p, char *aclstr) { pr_netacl_t *acl; char *cp, *aclstr_dup; if (p == NULL || aclstr == NULL) { errno = EINVAL; return NULL; } if (strlen(aclstr) == 0) { errno = EINVAL; return NULL; } /* Parse the given rule into a netacl object. */ acl = pcalloc(p, sizeof(pr_netacl_t)); aclstr_dup = pstrdup(p, aclstr); if (strncasecmp(aclstr, "all", 4) == 0) { aclstr_dup = pstrdup(p, "all"); acl->type = PR_NETACL_TYPE_ALL; } else if (strncasecmp(aclstr, "none", 5) == 0) { aclstr_dup = pstrdup(p, "none"); acl->type = PR_NETACL_TYPE_NONE; } else if ((cp = strchr(aclstr, '/')) != NULL) { char *tmp; acl->type = PR_NETACL_TYPE_IPMASK; *cp = '\0'; /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* We have some type of IP/mask, either IPv4 or IPv6. We know that colons * will only appear in IPv6 addresses, so... */ if (strspn(aclstr, "0123456789ABCDEFabcdef.:") != strlen(aclstr)) { errno = EINVAL; return NULL; } acl->addr = pr_netaddr_get_addr(p, aclstr, NULL); if (acl->addr == NULL) { return NULL; } /* Determine what the given bitmask is. */ acl->masklen = strtol(cp + 1, &tmp, 10); if (tmp && *tmp) { /* Invalid bitmask syntax. */ errno = EINVAL; return NULL; } *cp = '/'; /* Make sure the given mask length is appropriate for the address. */ switch (pr_netaddr_get_family(acl->addr)) { case AF_INET: { /* Make sure that the given number of bits is not more than supported * for IPv4 addresses (32). */ if (acl->masklen > 32) { errno = EINVAL; return NULL; } break; } #ifdef PR_USE_IPV6 case AF_INET6: { if (pr_netaddr_use_ipv6()) { if (acl->masklen > 128) { errno = EINVAL; return NULL; } else if (pr_netaddr_is_v4mappedv6(acl->addr) == TRUE && acl->masklen > 32) { /* The admin may be trying to use IPv6-style masks on IPv4-mapped * IPv6 addresses, which of course will not work as expected. * If the mask is 32 bits or more, warn the admin. */ pr_log_pri(PR_LOG_WARNING, "warning: possibly using IPv6-style netmask on IPv4-mapped IPv6 address, which will not work as expected"); pr_trace_msg(trace_channel, 1, "possibly using IPv6-style netmask on IPv4-mapped IPv6 address (%s), which will not work as expected", aclstr); break; } } } #endif /* PR_USE_IPV6 */ default: break; } #ifdef PR_USE_IPV6 } else if (pr_netaddr_use_ipv6() && strspn(aclstr, "0123456789ABCDEFabcdef.:!") != strlen(aclstr)) { #else } else if (strspn(aclstr, "0123456789.!") != strlen(aclstr)) { #endif /* PR_USE_IPV6 */ /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or if the * first character is a '.', then treat the rule as a glob. */ if (strpbrk(aclstr, "{[*?")) { register unsigned int i; size_t aclstr_len = strlen(aclstr); pr_netacl_type_t netacl_type = PR_NETACL_TYPE_IPGLOB; /* Is this a DNS glob, or an IP address glob? To find out, see if there * are any non-IP characters (i.e. alphabetical characters, taking IPv6 * into account). */ for (i = 0; i < aclstr_len; i++) { if (PR_ISALPHA(aclstr[i])) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { if (aclstr[i] == 'A' || aclstr[i] == 'a' || aclstr[i] == 'B' || aclstr[i] == 'b' || aclstr[i] == 'C' || aclstr[i] == 'c' || aclstr[i] == 'D' || aclstr[i] == 'd' || aclstr[i] == 'E' || aclstr[i] == 'e' || aclstr[i] == 'F' || aclstr[i] == 'f') { continue; } netacl_type = PR_NETACL_TYPE_DNSGLOB; break; } else { netacl_type = PR_NETACL_TYPE_DNSGLOB; break; } #else netacl_type = PR_NETACL_TYPE_DNSGLOB; break; #endif /* PR_USE_IPV6 */ } } acl->type = netacl_type; acl->pattern = pstrdup(p, aclstr); } else if (*aclstr == '.') { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrcat(p, "*", aclstr, NULL); } else { acl->type = PR_NETACL_TYPE_DNSMATCH; acl->pattern = pstrdup(p, aclstr); } } else if (strchr(aclstr, '.') == NULL) { /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or if the * first character is a '.', then treat the rule as a glob. */ if (strpbrk(aclstr, "{[*?")) { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrdup(p, aclstr); } else { acl->type = PR_NETACL_TYPE_DNSMATCH; acl->pattern = pstrdup(p, aclstr); } } else { /* Check if the given rule is negated. */ if (*aclstr == '!') { acl->negated = TRUE; aclstr++; } /* If the last character is a '.', then treat the rule as an IP glob. */ if (aclstr[strlen(aclstr)-1] == '.') { acl->type = PR_NETACL_TYPE_IPGLOB; acl->pattern = pstrcat(p, aclstr, "*", NULL); } else { register unsigned int i; int use_glob = FALSE, use_dns = FALSE; size_t aclstr_len; /* Is this a DNS glob, DNS match, IP glob, or IP match? * * First, check for any glob characters. After that, determine whether * it's a DNS or IP type ACL. */ /* If there are any glob characters (e.g. '{', '[', '*', or '?'), or * if the first character is a '.', then treat the rule as a glob. */ use_glob = (strpbrk(aclstr, "{[*?") != NULL); aclstr_len = strlen(aclstr); for (i = 0; i < aclstr_len; i++) { if (PR_ISALPHA(aclstr[i])) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { if (aclstr[i] == 'A' || aclstr[i] == 'a' || aclstr[i] == 'B' || aclstr[i] == 'b' || aclstr[i] == 'C' || aclstr[i] == 'c' || aclstr[i] == 'D' || aclstr[i] == 'd' || aclstr[i] == 'E' || aclstr[i] == 'e' || aclstr[i] == 'F' || aclstr[i] == 'f') { continue; } use_dns = TRUE; break; } else { use_dns = TRUE; break; } #else use_dns = TRUE; break; #endif /* PR_USE_IPV6 */ } } if (!use_dns) { acl->type = use_glob ? PR_NETACL_TYPE_IPGLOB : PR_NETACL_TYPE_IPMATCH; acl->addr = pr_netaddr_get_addr(p, aclstr, NULL); if (acl->addr == NULL) { return NULL; } } else { if (use_glob) { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrdup(p, aclstr); } else if (*aclstr == '.') { acl->type = PR_NETACL_TYPE_DNSGLOB; acl->pattern = pstrcat(p, "*", aclstr, NULL); } else { acl->type = PR_NETACL_TYPE_DNSMATCH; acl->pattern = pstrdup(p, aclstr); } } } } acl->aclstr = aclstr_dup; return acl; }
static int parse_version(char *version_str, unsigned int *version, unsigned int *version_status) { register unsigned int i; char c, *ptr, *tmp; int have_suffix = FALSE; size_t revision_len = 0; /* Parse the given version string. We expect to see: * * major[.minor[.revision[suffix]]] * * Examples: * * "1" * "1.3" * "1.3.3" * "1.3.3rc1" * "1.3.3a" */ /* Quick sanity check */ if (!PR_ISDIGIT(version_str[0])) { return -1; } /* Parse the major number */ ptr = strchr(version_str, '.'); if (ptr) { *ptr = '\0'; tmp = NULL; version[0] = (int) strtoul(version_str, &tmp, 10); if (tmp && *tmp) { *ptr = '.'; return -1; } *ptr = '.'; /* Make sure that there is a character following the period. */ if (*(ptr + 1) != '\0') { version_str = ptr + 1; } else { return -1; } } else { tmp = NULL; version[0] = (int) strtoul(version_str, &tmp, 10); if (tmp && *tmp) { return -1; } return 0; } /* Parse the minor number */ ptr = strchr(version_str, '.'); if (ptr) { *ptr = '\0'; tmp = NULL; version[1] = (int) strtoul(version_str, &tmp, 10); if (tmp && *tmp) { *ptr = '.'; return -1; } *ptr = '.'; /* Make sure that there is a character following the period. */ if (*(ptr + 1) != '\0') { version_str = ptr + 1; } else { return -1; } } else { tmp = NULL; version[1] = (int) strtoul(version_str, &tmp, 10); if (tmp && *tmp) { return -1; } return 0; } /* Parse the revision number. This is trickier, since we have to also * account for the suffix, and there is no delimiter between the revision * number and the suffix characters. * * We thus scan every character from here on out. If they are all digits, * then it is a "stable" release (no suffix). Otherwise, it's either an * RC release, a maintenance release, or it's a badly formatted version * string. */ for (i = 0; i < strlen(version_str); i++) { if (!PR_ISDIGIT(version_str[i])) { if (i > 0) { have_suffix = TRUE; break; } /* Syntax error */ return -1; } else { /* Keep track of the number of characters in the revision number; this * is handy for afterwards, assuming we do have a suffix. */ revision_len++; } } if (!have_suffix) { tmp = NULL; version[2] = (int) strtoul(version_str, &tmp, 10); if (tmp && *tmp) { return -1; } /* Stable release */ *version_status = IFVERSION_STATUS_STABLE; return 0; } ptr = version_str + revision_len; c = *ptr; *ptr = '\0'; tmp = NULL; version[2] = (int) strtoul(version_str, &tmp, 10); if (tmp && *tmp) { *ptr = c; return -1; } *ptr = c; /* We already know, based on the suffix check, that there are characters * after the revision number. */ version_str = ptr; /* If the next two characters are "rc" (case-insensitive) followed by * digits, it's an RC release. (If there are no digits, it's a syntax * error.) * * If there only a single character left, it is a maintenance release. */ if (strlen(version_str) == 1) { if (!PR_ISALPHA(version_str[0])) { /* Syntax error */ return -1; } /* Maintenance release. */ c = toupper(version_str[0]); *version_status = IFVERSION_STATUS_STABLE + (c - 'A'); return 0; } if (strncasecmp(version_str, "rc", 2) != 0) { return -1; } /* RC release */ *version_status = IFVERSION_STATUS_RC; if (strlen(version_str) == 2) { return 0; } version_str += 2; for (i = 0; i < strlen(version_str); i++) { if (!PR_ISDIGIT(version_str[i])) { /* Syntax error */ return -1; } } tmp = NULL; *version_status += strtoul(version_str, &tmp, 10); if (tmp && *tmp) { return -1; } return 0; }