chiark / gitweb /
Merge branches 'idx/verh' and 'idx/qmqpc'
[qmail] / addrcheck.c
CommitLineData
ca51b51d
MW
1#include "cdb.h"
2#include "stralloc.h"
3#include "byte.h"
4#include "str.h"
5#include "addrcheck.h"
6#include <errno.h>
0a045fc1 7#include <unistd.h>
ca51b51d
MW
8
9/* #define DEBUG */
10#ifdef DEBUG
11# define D(x) x
12# include <stdio.h>
13# include <sys/types.h>
ca51b51d
MW
14#else
15# define D(x)
16#endif
17
18#define STRALLOC_INIT { 0 }
19
20static int probe(int cdb, int prefix, const char *key, int len,
0a045fc1 21 const char *suffix, const char **kp, uint32 *dlen)
ca51b51d
MW
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) ||
0a045fc1
MW
30 (suffix && !stralloc_cats(&k, suffix)) ||
31 !stralloc_0(&k))
ca51b51d 32 return (-1);
0a045fc1 33 if (kp) *kp = k.s;
ca51b51d 34 D( fprintf(stderr, "*** `%.*s' -> ", k.len, k.s); )
0a045fc1 35 rc = cdb_seek(cdb, k.s, k.len - 1, dlen);
ca51b51d
MW
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
0a045fc1
MW
60static int localprobe(int cdb, const char *sender,
61 const char *key, int len,
62 const char *suffix, const char *tail, int *rc)
ca51b51d
MW
63{
64 int err;
65 uint32 dlen;
66 char ch;
0a045fc1
MW
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)
ca51b51d
MW
76 return (-1);
77 if (!err) { *rc = 0; return (0); }
0a045fc1 78 if (!dlen) { errno = EINVAL; return (-1); }
ca51b51d 79 if (read(cdb, &ch, 1) != 1) { errno = EIO; return (-1); }
0a045fc1
MW
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 }
ca51b51d
MW
120 *rc = ch;
121 return (1);
122}
123
0a045fc1
MW
124static int local(int cdb, const char *l, int len,
125 const char *sender, int *rc)
ca51b51d
MW
126{
127 int code;
128 int err = 0;
129 int dash;
130
0a045fc1
MW
131 if ((err = localprobe(cdb, sender, l, len, 0, l + len, &code)) != 0)
132 goto done;
ca51b51d
MW
133
134 for (;;) {
135 dash = byte_rchr(l, len, '-');
136 if (dash == len) break;
0a045fc1
MW
137 if ((err = localprobe(cdb, sender,
138 l, dash, "-default", l + dash + 1, &code)) != 0)
139 goto done;
ca51b51d
MW
140 len = dash;
141 }
142 *rc = 0;
143 return (0);
144
145done:
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
156static int virt(int cdb, const char *u, int ulen,
0a045fc1 157 const char *addr, int alen, const char *sender, int *rc)
ca51b51d
MW
158{
159 static stralloc l = STRALLOC_INIT;
160 uint32 dlen;
161 int err;
162
0a045fc1 163 if ((err = probe(cdb, 'V', addr, alen, 0, 0, &dlen)) <= 0)
ca51b51d
MW
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;
0a045fc1
MW
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);
ca51b51d
MW
172 return (1);
173}
174
0a045fc1 175int addrcheck(int cdb, const char *addr, const char *sender, int *rc)
ca51b51d
MW
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])
0a045fc1 184 return (local(cdb, addr, len, sender, rc));
ca51b51d 185
0a045fc1 186 if ((err = virt(cdb, addr, at, addr, len, sender, rc)) != 0)
ca51b51d
MW
187 return (err);
188 dot = at + 1;
189 while (addr[dot]) {
0a045fc1 190 if ((err = virt(cdb, addr, at, addr + dot, len - dot, sender, rc)) != 0)
ca51b51d
MW
191 return (err);
192 dot += byte_chr(addr + dot + 1, len - dot - 1, '.') + 1;
193 }
194
0a045fc1 195 if ((err = probe(cdb, '@', addr + at + 1, len - at - 1, 0, 0, &dlen)) < 0)
ca51b51d
MW
196 return (-1);
197 if (!err) { *rc = 1; return (0); }
198 if (dlen != 0) { errno = EINVAL; return (-1); }
199
0a045fc1 200 return (local(cdb, addr, at, sender, rc));
ca51b51d
MW
201}
202
203#ifdef TEST
0a045fc1 204
ca51b51d
MW
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
212int main(int argc, char *argv[])
213{
214 int fd;
215 int rc;
216 int i;
217
0a045fc1
MW
218 if (argc < 4) {
219 fprintf(stderr, "usage: addrcheck CDB SENDER ADDR...\n");
ca51b51d
MW
220 return (1);
221 }
222 if ((fd = open(argv[1], O_RDONLY)) < 0) {
223 perror(argv[1]);
224 return (1);
225 }
0a045fc1
MW
226 for (i = 3; i < argc; i++) {
227 if (addrcheck(fd, argv[i], argv[2], &rc) < 0) {
ca51b51d
MW
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