addrcheck: Optionally invoke userv service to decide address validity.
[qmail] / addrcheck.c
1 #include "cdb.h"
2 #include "stralloc.h"
3 #include "byte.h"
4 #include "str.h"
5 #include "addrcheck.h"
6 #include <errno.h>
7 #include <unistd.h>
8
9 /* #define DEBUG */
10 #ifdef DEBUG
11 # define D(x) x
12 # include <stdio.h>
13 # include <sys/types.h>
14 #else
15 # define D(x)
16 #endif
17
18 #define STRALLOC_INIT { 0 }
19
20 static int probe(int cdb, int prefix, const char *key, int len,
21 const char *suffix, const char **kp, uint32 *dlen)
22 {
23 static stralloc k = STRALLOC_INIT;
24 char ch = prefix;
25 int rc;
26
27 k.len = 0;
28 if (!stralloc_append(&k, &ch) ||
29 !stralloc_catb(&k, key, len) ||
30 (suffix && !stralloc_cats(&k, suffix)) ||
31 !stralloc_0(&k))
32 return (-1);
33 if (kp) *kp = k.s;
34 D( fprintf(stderr, "*** `%.*s' -> ", k.len, k.s); )
35 rc = cdb_seek(cdb, k.s, k.len - 1, dlen);
36 D( if (rc == -1)
37 fprintf(stderr, "error: %s\n", strerror(errno));
38 else if (rc == 0)
39 fprintf(stderr, "not found\n");
40 else if (!*dlen)
41 fprintf(stderr, "empty\n");
42 else {
43 int n = *dlen;
44 int nn;
45 char buf[256];
46 off_t pos = lseek(cdb, 0, SEEK_CUR);
47 fprintf(stderr, "`");
48 while (n) {
49 nn = sizeof(buf); if (nn > n) nn = n;
50 read(cdb, buf, nn);
51 fwrite(buf, 1, nn, stderr);
52 n -= nn;
53 }
54 fprintf(stderr, "'\n");
55 lseek(cdb, pos, SEEK_SET);
56 } )
57 return (rc);
58 }
59
60 static int localprobe(int cdb, const char *sender,
61 const char *key, int len,
62 const char *suffix, const char *tail, int *rc)
63 {
64 int err;
65 uint32 dlen;
66 char ch;
67 const char *k;
68 static stralloc u = STRALLOC_INIT;
69 static stralloc serv = STRALLOC_INIT;
70 int kid;
71 int n;
72 int wstat;
73 int p[2];
74
75 if ((err = probe(cdb, 'L', key, len, suffix, &k, &dlen)) < 0)
76 return (-1);
77 if (!err) { *rc = 0; return (0); }
78 if (!dlen) { errno = EINVAL; return (-1); }
79 if (read(cdb, &ch, 1) != 1) { errno = EIO; return (-1); }
80 if (ch == '?') {
81 u.len = 0;
82 if (!stralloc_ready(&u, dlen - 1)) return (-1);
83 if (read(cdb, u.s, dlen - 1) != dlen - 1) { errno = EIO; return (-1); }
84 u.len = dlen - 1;
85 serv.len = 0;
86 if (!stralloc_cats(&serv, "addrcheck:") ||
87 !stralloc_cats(&serv, k + 1) ||
88 !stralloc_0(&serv) ||
89 !stralloc_0(&u))
90 return (-1);
91 D( fprintf(stderr, "asking user:\n\
92 user = %s; service = %s\n tail = %s; sender = %s\n\
93 address = %s; key = %s\n",
94 u.s, serv.s, tail, sender, key, k + 1); )
95
96 if (pipe(p) || (kid = fork()) == -1)
97 return (-1);
98 if (!kid) {
99 dup2(p[1], 1);
100 close(p[0]);
101 close(p[1]);
102 execl("/usr/bin/userv", "/usr/bin/userv",
103 "-f", "stdin=/dev/null",
104 u.s, serv.s,
105 tail, sender, key, k + 1,
106 (char *)0);
107 _exit(127);
108 }
109 close(p[1]);
110 n = read(p[0], &ch, 1);
111 close(p[0]);
112 if (wait_pid(&wstat, kid) < 0) { return (-1); }
113 D( fprintf(stderr, "userv exited with status %d\n", wstat); )
114 if (n != 1 || wstat) { errno = EAGAIN; return (-1); }
115 D( fprintf(stderr, "userv answer was `%c'\n", ch); )
116 } else if (dlen != 1) {
117 errno = EIO;
118 return (-1);
119 }
120 *rc = ch;
121 return (1);
122 }
123
124 static int local(int cdb, const char *l, int len,
125 const char *sender, int *rc)
126 {
127 int code;
128 int err = 0;
129 int dash;
130
131 if ((err = localprobe(cdb, sender, l, len, 0, l + len, &code)) != 0)
132 goto done;
133
134 for (;;) {
135 dash = byte_rchr(l, len, '-');
136 if (dash == len) break;
137 if ((err = localprobe(cdb, sender,
138 l, dash, "-default", l + dash + 1, &code)) != 0)
139 goto done;
140 len = dash;
141 }
142 *rc = 0;
143 return (0);
144
145 done:
146 if (err >= 0) {
147 switch (code) {
148 case '+': *rc = 1; break;
149 case '-': *rc = 0; break;
150 default: errno = EINVAL; err = -1; break;
151 }
152 }
153 return (err);
154 }
155
156 static int virt(int cdb, const char *u, int ulen,
157 const char *addr, int alen, const char *sender, int *rc)
158 {
159 static stralloc l = STRALLOC_INIT;
160 uint32 dlen;
161 int err;
162
163 if ((err = probe(cdb, 'V', addr, alen, 0, 0, &dlen)) <= 0)
164 return (err);
165 if (!stralloc_ready(&l, dlen + 1)) return (-1);
166 if (read(cdb, l.s, dlen) != dlen) { errno = EIO; return (-1); }
167 l.s[dlen] = '-';
168 l.len = dlen + 1;
169 if (!stralloc_catb(&l, u, ulen) || !stralloc_0(&l)) return (-1);
170 D( printf("*** virtual map -> `%s'\n", l.s); )
171 if (local(cdb, l.s, l.len - 1, sender, rc) < 0) return (-1);
172 return (1);
173 }
174
175 int addrcheck(int cdb, const char *addr, const char *sender, int *rc)
176 {
177 int at, len, dot;
178 int err = 0;
179 uint32 dlen;
180
181 len = str_len(addr);
182 at = str_chr(addr, '@');
183 if (!addr[at])
184 return (local(cdb, addr, len, sender, rc));
185
186 if ((err = virt(cdb, addr, at, addr, len, sender, rc)) != 0)
187 return (err);
188 dot = at + 1;
189 while (addr[dot]) {
190 if ((err = virt(cdb, addr, at, addr + dot, len - dot, sender, rc)) != 0)
191 return (err);
192 dot += byte_chr(addr + dot + 1, len - dot - 1, '.') + 1;
193 }
194
195 if ((err = probe(cdb, '@', addr + at + 1, len - at - 1, 0, 0, &dlen)) < 0)
196 return (-1);
197 if (!err) { *rc = 1; return (0); }
198 if (dlen != 0) { errno = EINVAL; return (-1); }
199
200 return (local(cdb, addr, at, sender, rc));
201 }
202
203 #ifdef TEST
204
205 #include <sys/types.h>
206 #include <unistd.h>
207 #include <fcntl.h>
208 #include <stdio.h>
209 #include <errno.h>
210 #include <unistd.h>
211
212 int main(int argc, char *argv[])
213 {
214 int fd;
215 int rc;
216 int i;
217
218 if (argc < 4) {
219 fprintf(stderr, "usage: addrcheck CDB SENDER ADDR...\n");
220 return (1);
221 }
222 if ((fd = open(argv[1], O_RDONLY)) < 0) {
223 perror(argv[1]);
224 return (1);
225 }
226 for (i = 3; i < argc; i++) {
227 if (addrcheck(fd, argv[i], argv[2], &rc) < 0) {
228 perror("checking");
229 return (1);
230 }
231 printf("%s: %s\n", argv[i], rc ? "ok" : "bad");
232 }
233 return (0);
234 }
235
236 #endif