/* Mixmaster version 3 -- (C) 1999 Anonymizer Inc. Mixmaster may be redistributed and modified under certain conditions. This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the file COPYRIGHT for details. Socket-based mail transport services $Id: mail.c,v 1.1 2001/10/31 08:19:53 rabbi Exp $ */ #include "mix3.h" #include #include #include #if defined(UNIX) && defined(USE_SOCK) #include #include #include #include #include #include #include #include #endif int sendinfofile(char *name, char *logname, BUFFER *address, BUFFER *header) { FILE *f, *log = NULL; BUFFER *msg, *addr; char line[LINELEN]; int ret = -1; if (bufeq(address, ANONNAME)) return (0); /* don't reply to our own anon messages */ f = mix_openfile(name, "r"); if (f == NULL) return (-1); addr = buf_new(); rfc822_addr(address, addr); if (addr->length == 0) buf_set(addr, address), buf_nl(addr); if (logname != NULL) { if ((log = mix_openfile(logname, "r+")) != NULL) { /* log recipients to prevent mail loop */ while (fgets(line, sizeof(line), log) != NULL) if (strieq(line, addr->data)) goto end; } else if ((log = mix_openfile(logname, "w")) == NULL) { errlog(ERRORMSG, "Can't create %s.\n", logname); ret = -1; goto end; } fprintf(log, "%s", addr->data); } msg = buf_new(); if (header) buf_cat(msg, header), buf_nl(msg); while (fgets(line, sizeof(line), f) != NULL) { if (streq(line, "DESTINATION-BLOCK\n")) buf_appendf(msg, "destination-block %b", addr); else buf_appends(msg, line); } ret = sendmail(msg, REMAILERNAME, address); fclose(f); buf_free(msg); end: if (log) fclose(log); buf_free(addr); return (ret); } int smtpsend(BUFFER *head, BUFFER *message, char *from); int sendmail(BUFFER *message, char *from, BUFFER *address) { /* returns: 0: ok 1: problem, try again -1: failed */ BUFFER *head, *block; FILE *f; int err = -1; head = buf_new(); block = readdestblk( ); if ( !block ) block = buf_new( ); if (address != NULL && (address->length == 0 || doblock(address, block, 1) == -1)) goto end; if (from != NULL) { buf_setf(head, "From: %s", from); hdr_encode(head, 255); buf_nl(head); } if (address != NULL) buf_appendf(head, "To: %b\n", address); buf_rewind(message); if (SMTPRELAY[0]) err = smtpsend(head, message, from); else if (strieq(SENDMAIL, "outfile")) { char path[PATHMAX]; FILE *f = NULL; int i; for (i = 0; i < 10000; i++) { sprintf(path, "%s/out%i.txt", POOLDIR, i); f = fopen(path, "r"); if (f) fclose(f); else break; } f = fopen(path, "w"); if (f != NULL) { err = buf_write(head, f); err = buf_write(message, f); fclose(f); } else errlog(ERRORMSG, "Can't create %s!\n", path); } else { if (SENDANONMAIL[0] != '\0' && (from == NULL || streq(from, ANONNAME))) f = openpipe(SENDANONMAIL); else f = openpipe(SENDMAIL); if (f != NULL) { err = buf_write(head, f); err = buf_write(message, f); closepipe(f); } } if (err != 0) err = 1; /* error while sending, retry later */ end: buf_free(block); buf_free(head); return (err); } /* socket communication **********************************************/ #ifdef USE_SOCK #ifdef WIN32 WSADATA w; int sock_init() { if (WSAStartup(MAKEWORD(2, 0), &w) != 0) { errlog(ERRORMSG, "Unable to initialize WINSOCK.\n"); return 0; } return 1; } void sock_exit(void) { WSACleanup(); } #endif SOCKET opensocket(char *hostname, int port) { struct hostent *hp; struct sockaddr_in server; SOCKET s; if ((hp = gethostbyname(hostname)) == NULL) return (INVALID_SOCKET); memset((char *) &server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = *(unsigned long *) hp->h_addr; server.sin_port = htons((unsigned short) port); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s != INVALID_SOCKET) if (connect(s, (struct sockaddr *) &server, sizeof(server)) < 0) { closesocket(s); return (INVALID_SOCKET); } return (s); } #ifndef WIN32 int closesocket(SOCKET s) { return (close(s)); } #endif int sock_getline(SOCKET s, BUFFER *line) { char c; int ok; buf_clear(line); while ((ok = recv(s, &c, 1, 0)) > 0) { if (c == '\n') break; if (c != '\r') buf_appendc(line, c); } if (ok <= 0) return (-1); if (line->length == 0) return (1); return (0); } int sock_cat(SOCKET s, BUFFER *b) { int p = 0, n; do { n = send(s, b->data, b->length, 0); if (n < 0) return (-1); p += n; } while (p < b->length); return (0); } #else SOCKET opensocket(char *hostname, int port) { return (INVALID_SOCKET); } int closesocket(SOCKET s) { return (INVALID_SOCKET); } int sock_getline(SOCKET s, BUFFER *line) { return (-1); } int sock_cat(SOCKET s, BUFFER *b) { return (-1); } #endif /* send messages by SMTP ************************************************/ static int sock_getsmtp(SOCKET s, BUFFER *line) { int ret; do ret = sock_getline(s, line); while (line->length >= 4 && line->data[3] == '-'); return (ret); } SOCKET smtp_open(void) { int s = INVALID_SOCKET; BUFFER *line; #ifdef USE_SOCK if (SMTPRELAY[0] != '\0') s = opensocket(SMTPRELAY, 25); if (s != INVALID_SOCKET) { line = buf_new(); sock_getsmtp(s, line); if (line->data[0] != '2') { errlog(ERRORMSG, "SMTP relay not ready. %b\n", line); closesocket(s); s = INVALID_SOCKET; } else { errlog(DEBUGINFO, "Opening SMTP connection.\n"); buf_sets(line, "HELO "); if (HELONAME[0]) buf_appends(line, HELONAME); else if (strchr(ENVFROM, '@')) buf_appends(line, strchr(ENVFROM, '@') + 1); else { struct sockaddr_in sa; int len = sizeof(sa); struct hostent *hp; if (getsockname(s, (struct sockaddr *) &sa, &len) == 0 && (hp = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET)) != NULL) buf_appends(line, (char *) hp->h_name); else if (strchr(REMAILERADDR, '@')) buf_appends(line, strchr(REMAILERADDR, '@') + 1); else buf_appends(line, SHORTNAME); } buf_appends(line, "\r\n"); sock_cat(s, line); sock_getsmtp(s, line); if (line->data[0] != '2') { errlog(ERRORMSG, "SMTP relay refuses HELO: %b\n", line); closesocket(s); s = INVALID_SOCKET; } } buf_free(line); } #endif return (s); } int smtp_close(SOCKET s) { BUFFER *line; int ret = -1; #ifdef USE_SOCK line = buf_new(); buf_sets(line, "QUIT\r\n"); sock_cat(s, line); if (sock_getsmtp(s, line) == 0 && line->data[0] == '2') { errlog(DEBUGINFO, "Closing SMTP connection.\n"); ret = 0; } else errlog(WARNING, "SMTP quit failed: %b\n", line); closesocket(s); buf_free(line); #endif return (ret); } int smtp_send(SOCKET relay, BUFFER *head, BUFFER *message, char *from) { BUFFER *rcpt, *line, *field, *content; int ret = -1; #ifdef USE_SOCK line = buf_new(); field = buf_new(); content = buf_new(); rcpt = buf_new(); while (buf_getheader(head, field, content) == 0) if (bufieq(field, "to")) rfc822_addr(content, rcpt); buf_rewind(head); while (buf_getheader(message, field, content) == 0) { if (bufieq(field, "to") || bufieq(field, "cc") || bufieq(field, "bcc")) { rfc822_addr(content, rcpt); } if (!bufieq(field, "bcc")) buf_appendheader(head, field, content); } buf_nl(head); buf_clear(content); if (from) { buf_sets(line, from); rfc822_addr(line, content); buf_chop(content); } if (bufieq(content, REMAILERADDR) || bufieq(content, ANONADDR)) buf_clear(content); if (content->length == 0) buf_sets(content, ENVFROM[0] ? ENVFROM : ANONADDR); buf_setf(line, "MAIL FROM:<%b>\r\n", content); sock_cat(relay, line); sock_getsmtp(relay, line); if (!line->data[0] == '2') { errlog(ERRORMSG, "SMTP relay does not accept mail: %b\n", line); goto end; } while (buf_getline(rcpt, content) == 0) { buf_setf(line, "RCPT TO:<%b>\r\n", content); sock_cat(relay, line); sock_getsmtp(relay, line); if (bufleft(line, "421")) { errlog(ERRORMSG, "SMTP relay error: %b\n", line); goto end; } } buf_sets(line, "DATA\r\n"); sock_cat(relay, line); sock_getsmtp(relay, line); if (!bufleft(line, "354")) { errlog(WARNING, "SMTP relay does not accept message: %b\n", line); goto end; } while (buf_getline(head, line) >= 0) { buf_appends(line, "\r\n"); if (bufleft(line, ".")) { buf_setf(content, ".%b", line); buf_move(line, content); } sock_cat(relay, line); } while (buf_getline(message, line) >= 0) { buf_appends(line, "\r\n"); if (bufleft(line, ".")) { buf_setf(content, ".%b", line); buf_move(line, content); } sock_cat(relay, line); } buf_sets(line, ".\r\n"); sock_cat(relay, line); sock_getsmtp(relay, line); if (bufleft(line, "2")) ret = 0; else errlog(WARNING, "SMTP relay will not send message: %b\n", line); end: buf_free(line); buf_free(field); buf_free(content); buf_free(rcpt); #endif return (ret); } static SOCKET sendmail_state = INVALID_SOCKET; void sendmail_begin(void) { /* begin mail sending session */ if (sendmail_state == INVALID_SOCKET) sendmail_state = smtp_open(); } void sendmail_end(void) { /* end mail sending session */ if (sendmail_state != INVALID_SOCKET) { smtp_close(sendmail_state); sendmail_state = INVALID_SOCKET; } } int smtpsend(BUFFER *head, BUFFER *message, char *from) { SOCKET s; int ret = -1; if (sendmail_state != INVALID_SOCKET) ret = smtp_send(sendmail_state, head, message, from); else { s = smtp_open(); if (s != INVALID_SOCKET) { ret = smtp_send(s, head, message, from); smtp_close(s); } } return (ret); } /* retrieve mail with POP3 **********************************************/ #ifdef USE_SOCK int pop3_close(SOCKET s); #define POP3_ANY 0 #define POP3_APOP 1 #define POP3_PASS 2 SOCKET pop3_open(char *user, char *host, char *pass, int auth) { SOCKET server = INVALID_SOCKET; BUFFER *line; int authenticated = 0; char c, md[33]; line = buf_new(); server = opensocket(host, 110); if (server == INVALID_SOCKET) errlog(NOTICE, "Can't connect to POP3 server %s.\n", host); else { sock_getline(server, line); if (!bufleft(line, "+")) { errlog(WARNING, "No POP3 service at %s.\n", host); closesocket(server); } } if (server != INVALID_SOCKET) { errlog(DEBUGINFO, "Opening POP3 connection to %s.\n", host); do c = buf_getc(line); while (c != '<' && c != -1); while (c != '>' && c != -1) { buf_appendc(line, c); c = buf_getc(line); } if (c == '>' && (auth == POP3_ANY || auth == POP3_APOP)) { buf_appendc(line, c); buf_appends(line, pass); digest_md5(line, line); id_encode(line->data, md); buf_setf(line, "APOP %s %s\r\n", user, md); sock_cat(server, line); sock_getline(server, line); if (bufleft(line, "+")) authenticated = 1; else { errlog(auth == POP3_APOP ? ERRORMSG : NOTICE, "POP3 APOP auth at %s failed: %b\n", host, line); buf_sets(line, "QUIT\r\n"); sock_cat(server, line); closesocket(server); server = pop3_open(user, host, pass, POP3_PASS); goto end; } } if (!authenticated) { buf_setf(line, "USER %s\r\n", user); sock_cat(server, line); sock_getline(server, line); if (!bufleft(line, "+")) errlog(ERRORMSG, "POP3 USER command at %s failed: %b\n", host, line); else { buf_setf(line, "PASS %s\r\n", pass); sock_cat(server, line); sock_getline(server, line); if (bufleft(line, "+")) authenticated = 1; else errlog(ERRORMSG, "POP3 auth at %s failed: %b\n", host, line); } } if (!authenticated) { pop3_close(server); server = INVALID_SOCKET; } } end: buf_free(line); return (server); } int pop3_close(SOCKET s) { BUFFER *line; int ret = -1; line = buf_new(); buf_sets(line, "QUIT\r\n"); sock_cat(s, line); sock_getline(s, line); if (bufleft(line, "+")) { ret = 0; errlog(DEBUGINFO, "Closing POP3 connection.\n"); } else errlog(ERRORMSG, "POP3 QUIT failed:\n", line->data); buf_free(line); return (ret); } int pop3_stat(SOCKET s) { BUFFER *line; int val = -1; line = buf_new(); buf_sets(line, "STAT\r\n"); sock_cat(s, line); sock_getline(s, line); if (bufleft(line, "+")) sscanf(line->data, "+%*s %d", &val); buf_free(line); return (val); } int pop3_list(SOCKET s, int n) { BUFFER *line; int val = -1; line = buf_new(); buf_setf(line, "LIST %d\r\n", n); sock_cat(s, line); sock_getline(s, line); if (bufleft(line, "+")) sscanf(line->data, "+%*s %d", &val); buf_free(line); return (val); } int pop3_dele(SOCKET s, int n) { BUFFER *line; int ret = 0; line = buf_new(); buf_setf(line, "DELE %d\r\n", n); sock_cat(s, line); sock_getline(s, line); if (!bufleft(line, "+")) ret = -1; buf_free(line); return (ret); } int pop3_retr(SOCKET s, int n, BUFFER *msg) { BUFFER *line; int ret = -1; line = buf_new(); buf_clear(msg); buf_setf(line, "RETR %d\r\n", n); sock_cat(s, line); sock_getline(s, line); if (bufleft(line, "+")) { for (;;) { if (sock_getline(s, line) == -1) break; if (bufeq(line, ".")) { ret = 0; break; } else if (bufleft(line, ".")) { buf_append(msg, line->data + 1, line->length - 1); } else buf_cat(msg, line); buf_nl(msg); } } buf_free(line); return (ret); } void pop3get(void) { FILE *f; char cfg[LINELEN], user[LINELEN], host[LINELEN], pass[LINELEN], auth[5]; SOCKET server; BUFFER *line, *msg; int i = 0, num = 0; line = buf_new(); msg = buf_new(); f = mix_openfile(POP3CONF, "r"); if (f != NULL) while (fgets(cfg, sizeof(cfg), f) != NULL) { if (strchr(cfg, '@')) strchr(cfg, '@')[0] = ' '; if (sscanf(cfg, "%127s %127s %127s %4s", user, host, pass, auth) < 3) continue; i = POP3_ANY; if (strileft(auth, "apop")) i = POP3_APOP; if (strileft(auth, "pass")) i = POP3_PASS; server = pop3_open(user, host, pass, i); if (server != INVALID_SOCKET) { num = pop3_stat(server); if (num < 0) errlog(WARNING, "POP3 protocol error at %s.\n", host); else if (num == 0) errlog(DEBUGINFO, "No mail at %s.\n", host); else for (i = 1; i <= num; i++) { if (POP3SIZELIMIT > 0 && pop3_list(server, i) > POP3SIZELIMIT * 1024) { errlog(WARNING, "Over size message on %s.", host); if (POP3DEL == 1) pop3_dele(server, i); } else { if (pop3_retr(server, i, msg) == 0 && pool_add(msg, "inf") == 0) pop3_dele(server, i); else { errlog(WARNING, "POP3 error while getting mail from %s.", host); closesocket(server); goto end; } } } pop3_close(server); } } end: buf_free(line); buf_free(msg); } #endif