int match_string(MATCH_LIST *list, const char *string, const char *pattern) { const char *myname = "match_string"; DICT *dict; if (msg_verbose) msg_info("%s: %s ~? %s", myname, string, pattern); /* * Try dictionary lookup: exact match. */ if (MATCH_DICTIONARY(pattern)) { if ((dict = dict_handle(pattern)) == 0) msg_panic("%s: unknown dictionary: %s", myname, pattern); if (dict_get(dict, string) != 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 string match. */ if (strcasecmp(string, pattern) == 0) { return (1); } /* * No match found. */ return (0); }
static ARGV *match_list_parse(ARGV *list, char *string, int init_match) { const char *myname = "match_list_parse"; VSTRING *buf = vstring_alloc(10); VSTREAM *fp; const char *delim = " ,\t\r\n"; char *bp = string; char *start; char *item; char *map_type_name_flags; int match; #define OPEN_FLAGS O_RDONLY #define DICT_FLAGS (DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX) #define STR(x) vstring_str(x) /* * /filename contents are expanded in-line. To support !/filename we * prepend the negation operator to each item from the file. */ while ((start = mystrtokq(&bp, delim, "{}")) != 0) { if (*start == '#') { msg_warn("%s: comment at end of line is not supported: %s %s", myname, start, bp); break; } for (match = init_match, item = start; *item == '!'; item++) match = !match; if (*item == 0) msg_fatal("%s: no pattern after '!'", myname); if (*item == '/') { /* /file/name */ if ((fp = vstream_fopen(item, O_RDONLY, 0)) == 0) { vstring_sprintf(buf, "%s:%s", DICT_TYPE_NOFILE, item); /* XXX Should increment existing map refcount. */ if (dict_handle(STR(buf)) == 0) dict_register(STR(buf), dict_surrogate(DICT_TYPE_NOFILE, item, OPEN_FLAGS, DICT_FLAGS, "open file %s: %m", item)); argv_add(list, STR(buf), (char *) 0); } else { while (vstring_fgets(buf, fp)) if (vstring_str(buf)[0] != '#') list = match_list_parse(list, vstring_str(buf), match); if (vstream_fclose(fp)) msg_fatal("%s: read file %s: %m", myname, item); } } else if (MATCH_DICTIONARY(item)) { /* type:table */ vstring_sprintf(buf, "%s%s(%o,%s)", match ? "" : "!", item, OPEN_FLAGS, dict_flags_str(DICT_FLAGS)); map_type_name_flags = STR(buf) + (match == 0); /* XXX Should increment existing map refcount. */ if (dict_handle(map_type_name_flags) == 0) dict_register(map_type_name_flags, dict_open(item, OPEN_FLAGS, DICT_FLAGS)); argv_add(list, STR(buf), (char *) 0); } else { /* other pattern */ argv_add(list, match ? item : STR(vstring_sprintf(buf, "!%s", item)), (char *) 0); } } vstring_free(buf); return (list); }
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); }
int match_hostname(MATCH_LIST *list, const char *name, const char *pattern) { const char *myname = "match_hostname"; const char *pd; const char *entry; const char *next; int match; DICT *dict; if (msg_verbose) msg_info("%s: %s ~? %s", myname, name, pattern); /* * Try dictionary lookup: exact match and parent domains. * * Don't look up parent domain substrings with regexp maps etc. */ if (MATCH_DICTIONARY(pattern)) { if ((dict = dict_handle(pattern)) == 0) msg_panic("%s: unknown dictionary: %s", myname, pattern); match = 0; for (entry = name; *entry != 0; entry = next) { if (entry == name || (dict->flags & DICT_FLAG_FIXED)) { match = (dict_get(dict, entry) != 0); if (msg_verbose > 1) msg_info("%s: lookup %s:%s %s: %s", myname, dict->type, dict->name, entry, match ? "found" : "notfound"); if (match != 0) break; if ((list->error = dict->error) != 0) return (match_error(list, "%s:%s: table lookup problem", dict->type, dict->name)); } if ((next = strchr(entry + 1, '.')) == 0) break; if (list->flags & MATCH_FLAG_PARENT) next += 1; } return (match); } /* * Try an exact match with the host name. */ if (strcasecmp(name, pattern) == 0) { return (1); } /* * See if the pattern is a parent domain of the hostname. */ else { if (list->flags & MATCH_FLAG_PARENT) { pd = name + strlen(name) - strlen(pattern); if (pd > name && pd[-1] == '.' && strcasecmp(pd, pattern) == 0) return (1); } else if (pattern[0] == '.') { pd = name + strlen(name) - strlen(pattern); if (pd > name && strcasecmp(pd, pattern) == 0) return (1); } } return (0); }