1 /*        $NetBSD: match_service.c,v 1.2 2017/02/14 01:16:45 christos Exp $     */
2 
3 /*++
4 /* NAME
5 /*        match_service 3
6 /* SUMMARY
7 /*        simple master.cf service name.type pattern matcher
8 /* SYNOPSIS
9 /*        #include <match_service.h>
10 /*
11 /*        ARGV      *match_service_init(pattern_list)
12 /*        const char *pattern_list;
13 /*
14 /*        ARGV      *match_service_init_argv(pattern_list)
15 /*        char      **pattern_list;
16 /*
17 /*        int       match_service_match(list, name_type)
18 /*        ARGV      *list;
19 /*        const char *name_type;
20 /*
21 /*        void match_service_free(list)
22 /*        ARGV      *list;
23 /* DESCRIPTION
24 /*        This module implements pattern matching for Postfix master.cf
25 /*        services.  This is more precise than using domain_list(3),
26 /*        because match_service(3) won't treat a dotted service name
27 /*        as a domain hierarchy. Moreover, this module has the advantage
28 /*        that it does not drag in all the LDAP, SQL and other map
29 /*        lookup client code into programs that don't need it.
30 /*
31 /*        Each pattern is of the form "name/type" or "type", where
32 /*        "name" and "type" are the first two fields of a master.cf
33 /*        entry. Patterns are separated by whitespace and/or commas.
34 /*        Matches are case insensitive. Patterns are matched in the
35 /*        specified order, and the matching process stops at the first
36 /*        match.  In order to reverse the result of a pattern match,
37 /*        precede a pattern with an exclamation point (!).
38 /*
39 /*        For backwards compatibility, the form name.type is still
40 /*        supported.
41 /*
42 /*        match_service_init() parses the pattern list. The result
43 /*        must be passed to match_service_match() or match_service_free().
44 /*
45 /*        match_service_init_argv() provides an alternate interface
46 /*        for pre-parsed strings.
47 /*
48 /*        match_service_match() matches one service name.type string
49 /*        against the specified pattern list.
50 /*
51 /*        match_service_free() releases storage allocated by
52 /*        match_service_init().
53 /* DIAGNOSTICS
54 /*        Fatal error: out of memory, malformed pattern.
55 /*        Panic: malformed search string.
56 /* SEE ALSO
57 /*        domain_list(3) match domain names.
58 /* LICENSE
59 /* .ad
60 /* .fi
61 /*        The Secure Mailer license must be distributed with this software.
62 /* AUTHOR(S)
63 /*        Wietse Venema
64 /*        IBM T.J. Watson Research
65 /*        P.O. Box 704
66 /*        Yorktown Heights, NY 10598, USA
67 /*--*/
68 
69 /* System library. */
70 
71 #include <sys_defs.h>
72 #include <string.h>
73 
74 #ifdef STRCASECMP_IN_STRINGS_H
75 #include <strings.h>
76 #endif
77 
78 /* Utility library. */
79 
80 #include <msg.h>
81 #include <argv.h>
82 #include <mymalloc.h>
83 #include <stringops.h>
84 #include <match_service.h>
85 
86 /* match_service_compat - backwards compatibility */
87 
match_service_compat(ARGV * argv)88 static void match_service_compat(ARGV *argv)
89 {
90     char  **cpp;
91     char   *cp;
92 
93     for (cpp = argv->argv; *cpp; cpp++) {
94           if (strrchr(*cpp, '/') == 0 && (cp = strrchr(*cpp, '.')) != 0)
95               *cp = '/';
96     }
97 }
98 
99 /* match_service_init - initialize pattern list */
100 
match_service_init(const char * patterns)101 ARGV   *match_service_init(const char *patterns)
102 {
103     const char *delim = CHARS_COMMA_SP;
104     ARGV   *list = argv_alloc(1);
105     char   *saved_patterns = mystrdup(patterns);
106     char   *bp = saved_patterns;
107     const char *item;
108 
109     while ((item = mystrtok(&bp, delim)) != 0)
110           argv_add(list, item, (char *) 0);
111     argv_terminate(list);
112     myfree(saved_patterns);
113     match_service_compat(list);
114     return (list);
115 }
116 
117 /* match_service_init_argv - impedance adapter */
118 
match_service_init_argv(char ** patterns)119 ARGV   *match_service_init_argv(char **patterns)
120 {
121     ARGV   *list = argv_alloc(1);
122     char  **cpp;
123 
124     for (cpp = patterns; *cpp; cpp++)
125           argv_add(list, *cpp, (char *) 0);
126     argv_terminate(list);
127     match_service_compat(list);
128     return (list);
129 }
130 
131 /* match_service_match - match service name.type against pattern list */
132 
match_service_match(ARGV * list,const char * name_type)133 int     match_service_match(ARGV *list, const char *name_type)
134 {
135     const char *myname = "match_service_match";
136     const char *type;
137     char  **cpp;
138     char   *pattern;
139     int     match;
140 
141     /*
142      * Quick check for empty list.
143      */
144     if (list->argv[0] == 0)
145           return (0);
146 
147     /*
148      * Sanity check.
149      */
150     if ((type = strrchr(name_type, '/')) == 0 || *++type == 0)
151           msg_panic("%s: malformed service: \"%s\"; need \"name/type\" format",
152                       myname, name_type);
153 
154     /*
155      * Iterate over all patterns in the list, stop at the first match.
156      */
157     for (cpp = list->argv; (pattern = *cpp) != 0; cpp++) {
158           if (msg_verbose)
159               msg_info("%s: %s ~? %s", myname, name_type, pattern);
160           for (match = 1; *pattern == '!'; pattern++)
161               match = !match;
162           if (strcasecmp(strchr(pattern, '/') ? name_type : type, pattern) == 0) {
163               if (msg_verbose)
164                     msg_info("%s: %s: found match", myname, name_type);
165               return (match);
166           }
167     }
168     if (msg_verbose)
169           msg_info("%s: %s: no match", myname, name_type);
170     return (0);
171 }
172 
173 /* match_service_free - release storage */
174 
match_service_free(ARGV * list)175 void    match_service_free(ARGV *list)
176 {
177     argv_free(list);
178 }
179