chiark / gitweb /
Merge branches 'idx/verh' and 'idx/qmqpc'
[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