7 /* simple string or host pattern matching
9 /* #include <match_ops.h>
11 /* int match_string(flags, string, pattern)
13 /* const char *string;
14 /* const char *pattern;
16 /* int match_hostname(flags, name, pattern)
19 /* const char *pattern;
21 /* int match_hostaddr(flags, addr, pattern)
24 /* const char *pattern;
26 /* This module implements simple string and host name or address
27 /* matching. The matching process is case insensitive. If a pattern
28 /* has the form type:name, table lookup is used instead of string
29 /* or address comparison.
31 /* match_string() matches the string against the pattern, requiring
32 /* an exact (case-insensitive) match. The flags argument is not used.
34 /* match_hostname() matches the host name when the hostname matches
35 /* the pattern exactly, or when the pattern matches a parent domain
36 /* of the named host. The flags argument specifies the bit-wise OR
37 /* of zero or more of the following:
38 /* .IP MATCH_FLAG_PARENT
39 /* The hostname pattern foo.com matches itself and any name below
40 /* the domain foo.com. If this flag is cleared, foo.com matches itself
41 /* only, and .foo.com matches any name below the domain foo.com.
43 /* Specify MATCH_FLAG_NONE to request none of the above.
45 /* match_hostaddr() matches a host address when the pattern is
46 /* identical to the host address, or when the pattern is a net/mask
47 /* that contains the address. The mask specifies the number of
48 /* bits in the network part of the pattern. The flags argument is
53 /* The Secure Mailer license must be distributed with this software.
56 /* IBM T.J. Watson Research
58 /* Yorktown Heights, NY 10598, USA
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
69 #ifdef STRCASECMP_IN_STRINGS_H
73 /* Utility library. */
79 #include <match_ops.h>
80 #include <stringops.h>
81 #include <cidr_match.h>
83 #define MATCH_DICTIONARY(pattern) \
84 ((pattern)[0] != '[' && strchr((pattern), ':') != 0)
86 /* match_string - match a string literal */
88 int match_string(int unused_flags
, const char *string
, const char *pattern
)
90 const char *myname
= "match_string";
94 msg_info("%s: %s ~? %s", myname
, string
, pattern
);
97 * Try dictionary lookup: exact match.
99 if (MATCH_DICTIONARY(pattern
)) {
100 match
= (dict_lookup(pattern
, string
) != 0);
104 msg_fatal("%s: table lookup problem", pattern
);
109 * Try an exact string match.
111 if (strcasecmp(string
, pattern
) == 0) {
121 /* match_hostname - match a host by name */
123 int match_hostname(int flags
, const char *name
, const char *pattern
)
125 const char *myname
= "match_hostname";
133 msg_info("%s: %s ~? %s", myname
, name
, pattern
);
136 * Try dictionary lookup: exact match and parent domains.
138 * Don't look up parent domain substrings with regexp maps etc.
140 if (MATCH_DICTIONARY(pattern
)) {
141 if ((dict
= dict_handle(pattern
)) == 0)
142 msg_panic("%s: unknown dictionary: %s", myname
, pattern
);
144 for (entry
= name
; *entry
!= 0; entry
= next
) {
145 if (entry
== name
|| (dict
->flags
& DICT_FLAG_FIXED
)) {
146 match
= (dict_get(dict
, entry
) != 0);
148 msg_info("%s: lookup %s:%s %s: %s",
149 myname
, dict
->type
, dict
->name
, entry
,
150 match
? "found" : "notfound");
154 msg_fatal("%s: table lookup problem", pattern
);
156 if ((next
= strchr(entry
+ 1, '.')) == 0)
158 if (flags
& MATCH_FLAG_PARENT
)
165 * Try an exact match with the host name.
167 if (strcasecmp(name
, pattern
) == 0) {
172 * See if the pattern is a parent domain of the hostname.
175 if (flags
& MATCH_FLAG_PARENT
) {
176 pd
= name
+ strlen(name
) - strlen(pattern
);
177 if (pd
> name
&& pd
[-1] == '.' && strcasecmp(pd
, pattern
) == 0)
179 } else if (pattern
[0] == '.') {
180 pd
= name
+ strlen(name
) - strlen(pattern
);
181 if (pd
> name
&& strcasecmp(pd
, pattern
) == 0)
188 /* match_hostaddr - match host by address */
190 int match_hostaddr(int unused_flags
, const char *addr
, const char *pattern
)
192 const char *myname
= "match_hostaddr";
194 CIDR_MATCH match_info
;
198 msg_info("%s: %s ~? %s", myname
, addr
, pattern
);
200 #define V4_ADDR_STRING_CHARS "01234567890."
201 #define V6_ADDR_STRING_CHARS V4_ADDR_STRING_CHARS "abcdefABCDEF:"
203 if (addr
[strspn(addr
, V6_ADDR_STRING_CHARS
)] != 0)
207 * Try dictionary lookup. This can be case insensitive.
209 if (MATCH_DICTIONARY(pattern
)) {
210 if (dict_lookup(pattern
, addr
) != 0)
213 msg_fatal("%s: table lookup problem", pattern
);
218 * Try an exact match with the host address.
220 if (pattern
[0] != '[') {
221 if (strcasecmp(addr
, pattern
) == 0)
224 size_t addr_len
= strlen(addr
);
226 if (strncasecmp(addr
, pattern
+ 1, addr_len
) == 0
227 && strcmp(pattern
+ 1 + addr_len
, "]") == 0)
232 * Light-weight tests before we get into expensive operations.
234 * - Don't bother matching IPv4 against IPv6. Postfix transforms
235 * IPv4-in-IPv6 to native IPv4 form when IPv4 support is enabled in
236 * Postfix; if not, then Postfix has no business dealing with IPv4
239 * - Don't bother unless the pattern is either an IPv6 address or net/mask.
241 * We can safely skip IPv4 address patterns because their form is
242 * unambiguous and they did not match in the strcasecmp() calls above.
244 * XXX We MUST skip (parent) domain names, which may appear in NAMADR_LIST
245 * input, to avoid triggering false cidr_match_parse() errors.
247 * The last two conditions below are for backwards compatibility with
248 * earlier Postfix versions: don't abort with fatal errors on junk that
249 * was silently ignored (principle of least astonishment).
251 if (!strchr(addr
, ':') != !strchr(pattern
, ':')
252 || pattern
[strcspn(pattern
, ":/")] == 0
253 || pattern
[strspn(pattern
, V4_ADDR_STRING_CHARS
)] == 0
254 || pattern
[strspn(pattern
, V6_ADDR_STRING_CHARS
"[]/")] != 0)
258 * No escape from expensive operations: either we have a net/mask
259 * pattern, or we have an address that can have multiple valid
260 * representations (e.g., 0:0:0:0:0:0:0:1 versus ::1, etc.). The only way
261 * to find out if the address matches the pattern is to transform
262 * everything into to binary form, and to do the comparison there.
264 saved_patt
= mystrdup(pattern
);
265 if ((err
= cidr_match_parse(&match_info
, saved_patt
, (VSTRING
*) 0)) != 0)
266 msg_fatal("%s", vstring_str(err
));
268 return (cidr_match_execute(&match_info
, addr
) != 0);