static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why) { DICT_CIDR_ENTRY *rule; char *pattern; char *value; CIDR_MATCH cidr_info; MAI_HOSTADDR_STR hostaddr; /* * Split the rule into key and value. We already eliminated leading * whitespace, comments, empty lines or lines with whitespace only. This * means a null key can't happen but we will handle this anyway. */ pattern = p; while (*p && !ISSPACE(*p)) /* Skip over key */ p++; if (*p) /* Terminate key */ *p++ = 0; while (*p && ISSPACE(*p)) /* Skip whitespace */ p++; value = p; trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */ if (*pattern == 0) { vstring_sprintf(why, "no address pattern"); return (0); } if (*value == 0) { vstring_sprintf(why, "no lookup result"); return (0); } /* * Parse the pattern, destroying it in the process. */ if (cidr_match_parse(&cidr_info, pattern, why) != 0) return (0); /* * Bundle up the result. */ rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY)); rule->cidr_info = cidr_info; rule->value = mystrdup(value); if (msg_verbose) { if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, hostaddr.buf, sizeof(hostaddr.buf)) == 0) msg_fatal("inet_ntop: %m"); msg_info("dict_cidr_open: add %s/%d %s", hostaddr.buf, cidr_info.mask_shift, rule->value); } return (rule); }
int match_hostaddr(MATCH_LIST *list, const char *addr, const char *pattern) { const char *myname = "match_hostaddr"; char *saved_patt; CIDR_MATCH match_info; DICT *dict; VSTRING *err; int rc; if (msg_verbose) msg_info("%s: %s ~? %s", myname, addr, pattern); #define V4_ADDR_STRING_CHARS "01234567890." #define V6_ADDR_STRING_CHARS V4_ADDR_STRING_CHARS "abcdefABCDEF:" if (addr[strspn(addr, V6_ADDR_STRING_CHARS)] != 0) return (0); /* * Try dictionary lookup. This can be case insensitive. */ if (MATCH_DICTIONARY(pattern)) { if ((dict = dict_handle(pattern)) == 0) msg_panic("%s: unknown dictionary: %s", myname, pattern); if (dict_get(dict, addr) != 0) return (1); if ((list->error = dict->error) != 0) return (match_error(list, "%s:%s: table lookup problem", dict->type, dict->name)); return (0); } /* * Try an exact match with the host address. */ if (pattern[0] != '[') { if (strcasecmp(addr, pattern) == 0) return (1); } else { size_t addr_len = strlen(addr); if (strncasecmp(addr, pattern + 1, addr_len) == 0 && strcmp(pattern + 1 + addr_len, "]") == 0) return (1); } /* * Light-weight tests before we get into expensive operations. * * - Don't bother matching IPv4 against IPv6. Postfix transforms * IPv4-in-IPv6 to native IPv4 form when IPv4 support is enabled in * Postfix; if not, then Postfix has no business dealing with IPv4 * addresses anyway. * * - Don't bother unless the pattern is either an IPv6 address or net/mask. * * We can safely skip IPv4 address patterns because their form is * unambiguous and they did not match in the strcasecmp() calls above. * * XXX We MUST skip (parent) domain names, which may appear in NAMADR_LIST * input, to avoid triggering false cidr_match_parse() errors. * * The last two conditions below are for backwards compatibility with * earlier Postfix versions: don't abort with fatal errors on junk that * was silently ignored (principle of least astonishment). */ if (!strchr(addr, ':') != !strchr(pattern, ':') || pattern[strcspn(pattern, ":/")] == 0 || pattern[strspn(pattern, V4_ADDR_STRING_CHARS)] == 0 || pattern[strspn(pattern, V6_ADDR_STRING_CHARS "[]/")] != 0) return (0); /* * No escape from expensive operations: either we have a net/mask * pattern, or we have an address that can have multiple valid * representations (e.g., 0:0:0:0:0:0:0:1 versus ::1, etc.). The only way * to find out if the address matches the pattern is to transform * everything into to binary form, and to do the comparison there. */ saved_patt = mystrdup(pattern); err = cidr_match_parse(&match_info, saved_patt, (VSTRING *) 0); myfree(saved_patt); if (err != 0) { list->error = DICT_ERR_RETRY; rc = match_error(list, "%s", vstring_str(err)); vstring_free(err); return (rc); } return (cidr_match_execute(&match_info, addr) != 0); }
static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, int lineno, int nesting, VSTRING *why) { DICT_CIDR_ENTRY *rule; char *pattern; char *value; CIDR_MATCH cidr_info; MAI_HOSTADDR_STR hostaddr; int match = 1; /* * IF must be followed by a pattern. */ if (strncasecmp(p, "IF", 2) == 0 && !ISALNUM(p[2])) { p += 2; for (;;) { if (*p == '!') match = !match; else if (!ISSPACE(*p)) break; p++; } if (*p == 0) { vstring_sprintf(why, "no address pattern"); return (0); } trimblanks(p, 0)[0] = 0; /* Trim trailing blanks */ if (cidr_match_parse_if(&cidr_info, p, match, why) != 0) return (0); value = ""; } /* * ENDIF must not be followed by other text. */ else if (strncasecmp(p, "ENDIF", 5) == 0 && !ISALNUM(p[5])) { p += 5; while (*p && ISSPACE(*p)) /* Skip whitespace */ p++; if (*p != 0) { vstring_sprintf(why, "garbage after ENDIF"); return (0); } if (nesting == 0) { vstring_sprintf(why, "ENDIF without IF"); return (0); } cidr_match_endif(&cidr_info); value = ""; } /* * An address pattern. */ else { /* * Process negation operators. */ for (;;) { if (*p == '!') match = !match; else if (!ISSPACE(*p)) break; p++; } /* * Split the rule into key and value. We already eliminated leading * whitespace, comments, empty lines or lines with whitespace only. * This means a null key can't happen but we will handle this anyway. */ pattern = p; while (*p && !ISSPACE(*p)) /* Skip over key */ p++; if (*p) /* Terminate key */ *p++ = 0; while (*p && ISSPACE(*p)) /* Skip whitespace */ p++; value = p; trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */ if (*pattern == 0) { vstring_sprintf(why, "no address pattern"); return (0); } /* * Parse the pattern, destroying it in the process. */ if (cidr_match_parse(&cidr_info, pattern, match, why) != 0) return (0); if (*value == 0) { vstring_sprintf(why, "no lookup result"); return (0); } } /* * Bundle up the result. */ rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY)); rule->cidr_info = cidr_info; rule->value = mystrdup(value); rule->lineno = lineno; if (msg_verbose) { if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, hostaddr.buf, sizeof(hostaddr.buf)) == 0) msg_fatal("inet_ntop: %m"); msg_info("dict_cidr_open: add %s/%d %s", hostaddr.buf, cidr_info.mask_shift, rule->value); } return (rule); }