/* * 20111227 * Jan Mojzis * derived from qmail-1.03/qmail-remote.c * Public domain. */ #include #include #include #include #include "timeoutread.h" #include "timeoutwrite.h" #include "buffer.h" #include "stralloc.h" #include "fmt.h" #include "env.h" #include "scan.h" #include "str.h" #define HUGESMTPTEXT 5000 const char *helohost = 0; const char *remoteip = 0; void out(const char *s) { if (buffer_puts(buffer_1small, s) == -1) _exit(0); } void zero(void) { if (buffer_put(buffer_1small,"",1) == -1) _exit(0); } void zerodie(void) { zero(); buffer_flush(buffer_1small); _exit(0); } void temp_nomem(void) { out("ZOut of memory. (#4.3.0)\n"); zerodie(); } void temp_read(void) { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); } void perm_partialline(void) { out("D\ SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); } void perm_usage(void) { out("D\ I (qmail-rsmtp) was invoked improperly. (#5.3.5)\n"); zerodie(); } void outhost(void) { buffer_puts(buffer_1small, remoteip); } int flagcritical = 0; void dropped(void) { out("ZConnected to "); outhost(); out(" but connection died. "); if (flagcritical) out("Possible duplicate! "); out("(#4.4.2)\n"); zerodie(); } unsigned long timeout = 1200; int unixread(int fd, char *buf, unsigned int len) { int r; r = read(fd, buf, len); if (r == -1) temp_read(); return r; } int saferead(int fd, char *buf, unsigned int len) { int r; r = timeoutread(timeout, fd, buf, len); if (r <= 0) dropped(); return r; } int safewrite(int fd, char *buf, unsigned int len) { int r; r = timeoutwrite(timeout, fd, buf, len); if (r <= 0) dropped(); return r; } char inbuf[1024]; buffer in = BUFFER_INIT(unixread,0,inbuf,sizeof inbuf); char netreadspace[128]; buffer netread = BUFFER_INIT(saferead,6,netreadspace,sizeof netreadspace); char smtptospace[1024]; buffer smtpto = BUFFER_INIT(safewrite,7,smtptospace,sizeof smtptospace); stralloc smtptext = {0}; stralloc smtpline = {0}; void get(unsigned char *ch) { buffer_get(&netread,(char *)ch,1); if (*ch != '\r') { if (smtptext.len < HUGESMTPTEXT) { if (!stralloc_append(&smtptext,(char *)ch)) temp_nomem(); } if (smtpline.len < HUGESMTPTEXT) { if (!stralloc_append(&smtpline,(char *)ch)) temp_nomem(); } } } unsigned long smtpcode(void (*callback)(unsigned long, const char *)) { unsigned char ch; unsigned long code; if (!stralloc_copys(&smtptext,"")) temp_nomem(); get(&ch); code = ch - '0'; get(&ch); code = code * 10 + (ch - '0'); get(&ch); code = code * 10 + (ch - '0'); for (;;) { get(&ch); if (ch != '-') break; if (callback) if (!stralloc_copys(&smtpline,"")) temp_nomem(); while (ch != '\n') get(&ch); if (callback) if (!stralloc_0(&smtpline)) temp_nomem(); if (callback) callback(code, smtpline.s); get(&ch); get(&ch); get(&ch); } if (callback) if (!stralloc_copys(&smtpline,"")) temp_nomem(); while (ch != '\n') get(&ch); if (callback) if (!stralloc_0(&smtpline)) temp_nomem(); if (callback) callback(code, smtpline.s); return code; } void outsmtptext(void) { unsigned int i; if (smtptext.s) if (smtptext.len) { out("Remote host said: "); for (i = 0;i < smtptext.len;++i) if (!smtptext.s[i]) smtptext.s[i] = '?'; if (buffer_put(buffer_1small,smtptext.s,smtptext.len) == -1) _exit(0); smtptext.len = 0; } } void outsmtptext2(void) { unsigned int i; if (smtptext.s) if (smtptext.len) { buffer_puts(buffer_2, "Remote host said: "); for (i = 0;i < smtptext.len;++i) if (!smtptext.s[i]) smtptext.s[i] = '?'; buffer_put(buffer_2,smtptext.s,smtptext.len); smtptext.len = 0; } } void quit(const char *prepend, const char *append) { buffer_putsflush(&smtpto,"QUIT\r\n"); out(prepend); outhost(); out(append); out(".\n"); outsmtptext(); zerodie(); } void quit2(const char *prepend, const char *append) { buffer_putsflush(&smtpto,"QUIT\r\n"); buffer_puts(buffer_2, prepend); buffer_puts(buffer_2, remoteip); buffer_puts(buffer_2, append); buffer_puts(buffer_2, ".\n"); outsmtptext2(); buffer_flush(buffer_2); _exit(0); } void blast(void) { int r; char ch; for (;;) { r = buffer_get(&in,&ch,1); if (r == 0) break; if (ch == '.') buffer_puts(&smtpto, "."); while (ch != '\n') { buffer_put(&smtpto, &ch, 1); r = buffer_get(&in,&ch,1); if (r == 0) perm_partialline(); } buffer_puts(&smtpto, "\r\n"); } flagcritical = 1; buffer_puts(&smtpto, ".\r\n"); buffer_flush(&smtpto); } int flagsize = 0; void ehlo_callback(unsigned long code, const char *line) { if (code != 250) return; if (str_start(line, "SIZE ")) flagsize = 1; return; } char strnum[FMT_LONGLONG]; int main(int argc, char **argv) { unsigned long code; unsigned int i; struct stat st; int flagbother; const char *x; if (argc < 3) perm_usage(); signal(SIGPIPE,SIG_IGN); /* get timeout */ x = env_get("TIMEOUT"); if (x) scan_ulong(x, &timeout); if (timeout < 1) timeout = 1; if (timeout > 3600) timeout = 3600; /* get helohost */ if (!helohost) helohost = env_get("HELOHOST"); if (!helohost) helohost = ""; /* get remoteip */ if (!remoteip) remoteip = env_get("REMOTEIP"); if (!remoteip) remoteip = "0"; code = smtpcode(0); if (code >= 500) quit("ZConnected to "," but greeting failed"); if (code != 220) quit2("Connected to "," but greeting failed"); /* XXX: try next MX */ buffer_puts(&smtpto, "EHLO "); buffer_puts(&smtpto, helohost); buffer_puts(&smtpto, "\r\n"); buffer_flush(&smtpto); code = smtpcode(ehlo_callback); if (code != 250) { buffer_puts(&smtpto, "HELO "); buffer_puts(&smtpto, helohost); buffer_puts(&smtpto, "\r\n"); buffer_flush(&smtpto); code = smtpcode(0); if (code != 250) quit("ZConnected to "," but my name was rejected"); } buffer_puts(&smtpto, "MAIL FROM:<"); buffer_puts(&smtpto, argv[1]); buffer_puts(&smtpto, ">"); if (flagsize && fstat(0, &st) == 0) { buffer_puts(&smtpto, " SIZE="); buffer_put(&smtpto, strnum, fmt_ulonglong(strnum, st.st_size)); } buffer_puts(&smtpto, "\r\n"); buffer_flush(&smtpto); code = smtpcode(0); if (code >= 500) quit("DConnected to "," but sender was rejected"); if (code >= 400) quit("ZConnected to "," but sender was rejected"); i = 2; flagbother = 0; while (argv[i]) { buffer_puts(&smtpto, "RCPT TO:<"); buffer_puts(&smtpto, argv[i]); buffer_puts(&smtpto, ">\r\n"); buffer_flush(&smtpto); code = smtpcode(0); if (code >= 500) { out("h"); outhost(); out(" does not like recipient.\n"); outsmtptext(); zero(); } else if (code >= 400) { out("s"); outhost(); out(" does not like recipient.\n"); outsmtptext(); zero(); } else { out("r"); zero(); flagbother = 1; } ++i; } if (!flagbother) quit("DGiving up on ",""); buffer_putsflush(&smtpto,"DATA\r\n"); code = smtpcode(0); if (code >= 500) quit("D"," failed on DATA command"); if (code >= 400) quit("Z"," failed on DATA command"); blast(); code = smtpcode(0); flagcritical = 0; if (code >= 500) quit("D"," failed after I sent the message"); if (code >= 400) quit("Z"," failed after I sent the message"); quit("K"," accepted message"); }