#include #include #include #include #include #include #include #include #include "extern.h" #include "cdb.h" #include "stralloc.h" #include "buffer.h" #include "open.h" #include "str.h" #include "byte.h" /* * qmail-banner * * Invoked by tcpserver prior to qmail-smtpd, optionally emits a banner * and pauses before invoking the rest of the command line. */ #ifndef VARQMAIL #define VARQMAIL "/var/qmail" #endif struct cdb c; void out(char *s, int cnt) { if (buffer_putalign(buffer_1, s, cnt)) exit(53); } void outs(char *s) { if (buffer_putsalign(buffer_1, s)) exit(53); } void outf(void) { if (buffer_flush(buffer_1)) exit(53); } void outsa(stralloc *sa) { char ch, *s; int cnt; s = sa->s; cnt = sa->len; out("220-", 4); while (cnt-- > 0) { ch = *s++; if (ch == '\n') { out("\r\n", 2); outf(); if (cnt) out("220-", 4); continue; } out(&ch, 1); } } void nomem(void) { outs("421 out of memory(#4.3.0)\r\n"); outf(); _exit(1); } struct { char *name; int len; } skipenv[] = { { "BANNER", 6 }, { "BANNERFILE", 10 }, { "BANNERPRESLEEP", 14 }, { "BANNERPOSTSLEEP",15 }, }; void modifyenv(void) { char **env; extern char **environ; stralloc newenv = {0}; int i, j; for (env = environ; *env; ++env) { if (byte_diff(*env, 6, "BANNER") != 0) continue; for (i = 0; i < sizeof(skipenv) / sizeof(skipenv[0]); ++i) if (byte_diff(*env, skipenv[i].len, skipenv[i].name) == 0 && env[0][skipenv[i].len] == '=') break; if (i != sizeof(skipenv) / sizeof(skipenv[0])) continue; if (!stralloc_cats(&newenv, *env + 6)) nomem(); if (!stralloc_0(&newenv)) nomem(); } if (!newenv.s) return; for (i = j = 0; i < newenv.len; ) { if (newenv.s[i++]) continue; if (putenv(newenv.s + j) < 0) nomem(); j = i; } } /* * Wait for the number of seconds. If the client * sends anything while waiting or closes the * connection, set RBLSMTPD and return 1. */ int bannersleep(char *p) { fd_set rfds, efds; struct timeval timeout; time_t t; if ((t = atol(p)) < 0 || t > 600) return 0; FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(0, &rfds); FD_SET(0, &efds); FD_SET(1, &efds); timeout.tv_sec = t; timeout.tv_usec = 0; if (select(2, &rfds, NULL, &efds, &timeout) > 0) { modifyenv(); return 1; } return 0; } void sendbanner(stralloc *sa) { varsub(sa, NULL); if (sa->s[sa->len - 1] != '\n') if (!stralloc_append(sa, "\n")) nomem(); outsa(sa); } int main(int argc, char **argv) { char *p, *ip; int fd, cc, found, dlen, bannerdone, len; int i, j, k; char ch; stralloc buf = { 0 }; chdir(VARQMAIL); signal(SIGPIPE, SIG_IGN); /* * Load environment variables from supplied arguments (if any) */ ip = getenv("TCPREMOTEIP"); found = 0; while (1) { cc = getopt(argc, argv, "+x:"); if (cc == -1) break; if (!ip) continue; if (found) continue; if ((fd = open_read(optarg)) < 0) continue; cdb_init(&c, fd); len = str_chr(ip, 0); while (1) { cc = cdb_find(&c, ip, len); if (cc > 0) { dlen = cdb_datalen(&c); if (!stralloc_ready(&buf, dlen + 1)) nomem(); cdb_read(&c, buf.s, dlen, cdb_datapos(&c)); buf.len = dlen; buf.s[dlen] = 0; for (i = 0; (j = i + str_chr(buf.s + i, 0)) < dlen; i = ++j) { if (buf.s[i] != '+') continue; ++i; k = i + str_chr(buf.s + i, '='); if (buf.s[k] == '=') { buf.s[k++] = 0; if (setenv(buf.s + i, buf.s + k, 1) == -1) nomem(); } else { unsetenv(buf.s + i); } } found = 1; break; } if (!len) break; while (--len) if (ip[len - 1] == '.') break; } cdb_free(&c); close(fd); } /* * if $BANNERPRESLEEP is set, wait that many seconds * before sending the banner. If the client sends * anything in that time, unset BANNERPOSTSLEEP so * we don't have to wait before punishing the client. */ bannerdone = 0; if ((p = getenv("BANNERPRESLEEP")) != NULL && *p) if (bannersleep(p)) { unsetenv("BANNERPOSTSLEEP"); bannerdone = 1; } /* * If $BANNER is set, send it to the client */ if (!bannerdone && (p = getenv("BANNER")) != NULL && *p) { if (!stralloc_copys(&buf, p)) nomem(); sendbanner(&buf); bannerdone = 1; } /* * If $BANNERFILE is set and we haven't already done a * banner, read in the contents of the file and send * it to the client */ if (!bannerdone && (p = getenv("BANNERFILE")) != NULL && *p) { if ((fd = open_read(p)) >= 0) { char b[4096]; buffer input; buffer_init(&input, read, fd, b, sizeof b); if (!stralloc_copys(&buf, "")) nomem(); while (1) { if (buffer_get(&input, &ch, 1) != 1) break; if (!stralloc_append(&buf, &ch)) nomem(); } close(fd); sendbanner(&buf); } } /* * If $BANNERPOSTSLEEP is set, pause. */ if ((p = getenv("BANNERPOSTSLEEP")) != NULL && *p) bannersleep(p); if (argv[optind]) execvp(argv[optind], argv + optind); return 0; }