X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/qmail/blobdiff_plain/7a9e083dd1ed3023ea1f6487760fa62676e987b8..ca51b51d75ecbf78f6c5786b6d3379d296e89d85:/addrcheck.c diff --git a/addrcheck.c b/addrcheck.c new file mode 100644 index 0000000..2ff9d13 --- /dev/null +++ b/addrcheck.c @@ -0,0 +1,181 @@ +#include "cdb.h" +#include "stralloc.h" +#include "byte.h" +#include "str.h" +#include "addrcheck.h" +#include + +/* #define DEBUG */ +#ifdef DEBUG +# define D(x) x +# include +# include +# include +#else +# define D(x) +#endif + +#define STRALLOC_INIT { 0 } + +static int probe(int cdb, int prefix, const char *key, int len, + const char *suffix, uint32 *dlen) +{ + static stralloc k = STRALLOC_INIT; + char ch = prefix; + int rc; + + k.len = 0; + if (!stralloc_append(&k, &ch) || + !stralloc_catb(&k, key, len) || + (suffix && !stralloc_cats(&k, suffix))) + return (-1); + D( fprintf(stderr, "*** `%.*s' -> ", k.len, k.s); ) + rc = cdb_seek(cdb, k.s, k.len, dlen); + D( if (rc == -1) + fprintf(stderr, "error: %s\n", strerror(errno)); + else if (rc == 0) + fprintf(stderr, "not found\n"); + else if (!*dlen) + fprintf(stderr, "empty\n"); + else { + int n = *dlen; + int nn; + char buf[256]; + off_t pos = lseek(cdb, 0, SEEK_CUR); + fprintf(stderr, "`"); + while (n) { + nn = sizeof(buf); if (nn > n) nn = n; + read(cdb, buf, nn); + fwrite(buf, 1, nn, stderr); + n -= nn; + } + fprintf(stderr, "'\n"); + lseek(cdb, pos, SEEK_SET); + } ) + return (rc); +} + +static int localprobe(int cdb, const char *key, int len, + const char *suffix, int *rc) +{ + int err; + uint32 dlen; + char ch; + + if ((err = probe(cdb, 'L', key, len, suffix, &dlen)) < 0) + return (-1); + if (!err) { *rc = 0; return (0); } + if (dlen != 1) { errno = EINVAL; return (-1); } + if (read(cdb, &ch, 1) != 1) { errno = EIO; return (-1); } + *rc = ch; + return (1); +} + +static int local(int cdb, const char *l, int len, int *rc) +{ + int code; + int err = 0; + int dash; + + if ((err = localprobe(cdb, l, len, 0, &code)) != 0) goto done; + + for (;;) { + dash = byte_rchr(l, len, '-'); + if (dash == len) break; + if ((err = localprobe(cdb, l, dash, "-default", &code)) != 0) goto done; + len = dash; + } + *rc = 0; + return (0); + +done: + if (err >= 0) { + switch (code) { + case '+': *rc = 1; break; + case '-': *rc = 0; break; + default: errno = EINVAL; err = -1; break; + } + } + return (err); +} + +static int virt(int cdb, const char *u, int ulen, + const char *addr, int alen, int *rc) +{ + static stralloc l = STRALLOC_INIT; + uint32 dlen; + int err; + + if ((err = probe(cdb, 'V', addr, alen, 0, &dlen)) <= 0) + return (err); + if (!stralloc_ready(&l, dlen + 1)) return (-1); + if (read(cdb, l.s, dlen) != dlen) { errno = EIO; return (-1); } + l.s[dlen] = '-'; + l.len = dlen + 1; + if (!stralloc_catb(&l, u, ulen)) return (-1); + D( printf("*** virtual map -> `%.*s'\n", l.len, l.s); ) + if (local(cdb, l.s, l.len, rc) < 0) return (-1); + return (1); +} + +int addrcheck(int cdb, const char *addr, int *rc) +{ + int at, len, dot; + int err = 0; + uint32 dlen; + + len = str_len(addr); + at = str_chr(addr, '@'); + if (!addr[at]) + return (local(cdb, addr, len, rc)); + + if ((err = virt(cdb, addr, at, addr, len, rc)) != 0) + return (err); + dot = at + 1; + while (addr[dot]) { + if ((err = virt(cdb, addr, at, addr + dot, len - dot, rc)) != 0) + return (err); + dot += byte_chr(addr + dot + 1, len - dot - 1, '.') + 1; + } + + if ((err = probe(cdb, '@', addr + at + 1, len - at - 1, 0, &dlen)) < 0) + return (-1); + if (!err) { *rc = 1; return (0); } + if (dlen != 0) { errno = EINVAL; return (-1); } + + return (local(cdb, addr, at, rc)); +} + +#ifdef TEST +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd; + int rc; + int i; + + if (argc < 3) { + fprintf(stderr, "usage: addrcheck CDB ADDR...\n"); + return (1); + } + if ((fd = open(argv[1], O_RDONLY)) < 0) { + perror(argv[1]); + return (1); + } + for (i = 2; i < argc; i++) { + if (addrcheck(fd, argv[i], &rc) < 0) { + perror("checking"); + return (1); + } + printf("%s: %s\n", argv[i], rc ? "ok" : "bad"); + } + return (0); +} + +#endif