/* smtp stuff handler for win32. * * (C) 2003-2024 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. * * Based on the original code of Epona by Lara. * Based on the original code of Services by Andy Church. * * Written by Dominick Meglio * *nix port by Trystan Scott Lee */ #include "sysconf.h" /* Some Linux boxes (or maybe glibc includes) require this for the * prototype of strsignal(). */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 # include # include # include # include # include # include #else # include # define WIN32_LEAN_AND_MEAN # include #endif #include #ifdef _AIX extern int strcasecmp(const char *, const char *); extern int strncasecmp(const char *, const char *, size_t); # if 0 /* These break on some AIX boxes (4.3.1 reported). */ extern int socket(int, int, int); extern int connect(int, struct sockaddr *, int); # endif #endif /* _AIX */ /* Some SUN fixs */ #ifdef __sun /* Solaris specific code, types that do not exist in Solaris' * * sys/types.h * **/ # ifndef INADDR_NONE # define INADDR_NONE (-1) # endif #endif #ifdef _WIN32 typedef SOCKET ano_socket_t; #define ano_sockclose(fd) closesocket(fd) #define ano_sockread(fd, buf, len) recv(fd, buf, len, 0) #define ano_sockwrite(fd, buf, len) send(fd, buf, len, 0) #else typedef int ano_socket_t; #define ano_sockclose(fd) close(fd) #define ano_sockread(fd, buf, len) read(fd, buf, len) #define ano_sockwrite(fd, buf, len) write(fd, buf, len) #define SOCKET_ERROR -1 #endif /* Data structures */ struct smtp_message { std::vector smtp_headers; std::vector smtp_body; std::string from; std::string to; ano_socket_t sock; }; int smtp_debug = 0; struct smtp_message smail; static std::string get_logname(struct tm *tm = NULL) { char timestamp[32]; if (!tm) { time_t t = time(NULL); tm = localtime(&t); } strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm); std::string name = std::string("anopesmtp.") + timestamp; return name; } /* TimeStamp for Email Header */ static std::string GetTimeStamp() { char tbuf[256]; time_t t = time(NULL); struct tm *tm = gmtime(&t); strftime(tbuf, sizeof(tbuf) - 1, "%a, %d %b %Y %H:%M:%S +0000", tm); return tbuf; } /* Log stuff to the log file with a datestamp. Note that errno is * preserved by this routine and log_perror(). */ void alog(const char *fmt, ...) { if (!smtp_debug || !fmt) return; std::fstream file; file.open(get_logname().c_str(), std::ios_base::out | std::ios_base::app); if (!file.is_open()) return; va_list args; va_start(args, fmt); time_t t = time(NULL); struct tm *tm = localtime(&t); char buf[256]; strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", tm); file << buf; vsnprintf(buf, sizeof(buf), fmt, args); file << buf << std::endl; va_end(args); va_end(args); file.close(); } /* Remove a trailing \r\n */ std::string strip(const std::string &buf) { std::string newbuf = buf; char c = newbuf[newbuf.size() - 1]; while (c == '\n' || c == '\r') { newbuf.erase(newbuf.end() - 1); c = newbuf[newbuf.size() - 1]; } return newbuf; } /* Is the buffer a header? */ bool smtp_is_header(const std::string &buf) { size_t tmp = buf.find(' '); if (tmp == std::string::npos) return false; if (tmp > 0 && buf[tmp - 1] == ':') return true; return false; } /* Parse a header into a name and value */ void smtp_parse_header(const std::string &buf, std::string &header, std::string &value) { std::string newbuf = strip(buf); size_t space = newbuf.find(' '); if (space != std::string::npos) { header = newbuf.substr(0, space); value = newbuf.substr(space + 1); } else { header = newbuf; value = ""; } } /* Have we reached the end of input? */ bool smtp_is_end(const std::string &buf) { if (buf[0] == '.') if (buf[1] == '\r' || buf[1] == '\n') return true; return false; } /* Set who the email is to */ void smtp_set_to(const std::string &to) { smail.to = to; size_t c = smail.to.rfind('<'); if (c != std::string::npos && c + 1 < smail.to.size()) { smail.to = smail.to.substr(c + 1); smail.to.erase(smail.to.end() - 1); } } /* Establish a connection to the SMTP server */ int smtp_connect(const char *host, unsigned short port) { struct sockaddr_in addr; if ((smail.sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) return 0; if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { struct hostent *hent; if (!(hent = gethostbyname(host))) return 0; memcpy(&addr.sin_addr, hent->h_addr, hent->h_length); } addr.sin_family = AF_INET; addr.sin_port = htons(port ? port : 25); if (connect(smail.sock, reinterpret_cast(&addr), sizeof(struct sockaddr_in)) == SOCKET_ERROR) { ano_sockclose(smail.sock); return 0; } return 1; } /* Send a line of text */ int smtp_send(const char *text) { int result = ano_sockwrite(smail.sock, text, strlen(text)); alog("SMTP: sent %s",text); if (result == SOCKET_ERROR) ano_sockclose(smail.sock); return result; } /* Read a line of text */ int smtp_read(char *buf, int len) { int result; memset(buf, 0, len); result = ano_sockread(smail.sock, buf, len); if (result == SOCKET_ERROR) ano_sockclose(smail.sock); return result; } /* Retrieve a response code */ int smtp_get_code(const std::string &text) { size_t tmp = text.find(' '); if (tmp == std::string::npos) return 0; return atol(text.substr(0, tmp).c_str()); } /* Send the email */ int smtp_send_email() { char buf[1024]; if (!smtp_read(buf, 1024)) { alog("SMTP: error reading buffer"); return 0; } int code = smtp_get_code(buf); if (code != 220) { alog("SMTP: error expected code 220 got %d",code); return 0; } if (!smtp_send("HELO anope\r\n")) { alog("SMTP: error writing to socket"); return 0; } if (!smtp_read(buf, 1024)) { alog("SMTP: error reading buffer"); return 0; } code = smtp_get_code(buf); if (code != 250) { alog("SMTP: error expected code 250 got %d",code); return 0; } strcpy(buf, "MAIL FROM: <"); strcat(buf, smail.from.c_str()); strcat(buf, ">\r\n"); if (!smtp_send(buf)) { alog("SMTP: error writing to socket"); return 0; } if (!smtp_read(buf, 1024)) { alog("SMTP: error reading buffer"); return 0; } code = smtp_get_code(buf); if (code != 250) return 0; strcpy(buf, "RCPT TO: <"); strcat(buf, smail.to.c_str()); strcat(buf, ">\r\n"); if (!smtp_send(buf)) { alog("SMTP: error writing to socket"); return 0; } if (!smtp_read(buf, 1024)) { alog("SMTP: error reading buffer"); return 0; } code = smtp_get_code(buf); if (smtp_get_code(buf) != 250) { alog("SMTP: error expected code 250 got %d",code); return 0; } if (!smtp_send("DATA\r\n")) { alog("SMTP: error writing to socket"); return 0; } if (!smtp_read(buf, 1024)) { alog("SMTP: error reading buffer"); return 0; } code = smtp_get_code(buf); if (code != 354) { alog("SMTP: error expected code 354 got %d",code); return 0; } for (std::vector::const_iterator it = smail.smtp_headers.begin(), it_end = smail.smtp_headers.end(); it != it_end; ++it) if (!smtp_send(it->c_str())) { alog("SMTP: error writing to socket"); return 0; } if (!smtp_send("\r\n")) { alog("SMTP: error writing to socket"); return 0; } bool skip_done = false; for (std::vector::const_iterator it = smail.smtp_body.begin(), it_end = smail.smtp_body.end(); it != it_end; ++it) if (skip_done) { if (!smtp_send(it->c_str())) { alog("SMTP: error writing to socket"); return 0; } } else skip_done = true; if (!smtp_send("\r\n.\r\n")) { alog("SMTP: error writing to socket"); return 0; } if (!smtp_read(buf, 1024)) { alog("SMTP: error reading buffer"); return 0; } code = smtp_get_code(buf); if (code != 250) { alog("SMTP: error expected code 250 got %d",code); return 0; } return 1; } void smtp_disconnect() { char buf[1024]; if (!smtp_send("QUIT\r\n")) { alog("SMTP: error writing to socket"); } if (!smtp_read(buf, 1024)) { alog("SMTP: error reading buffer"); } int code = smtp_get_code(buf); if (code != 221) { alog("SMTP: error expected code 221 got %d",code); } ano_sockclose(smail.sock); } int main(int argc, char *argv[]) { /* Win32 stuff */ #ifdef _WIN32 WSADATA wsa; #endif if (argc == 1) return 0; if (argc == 3 && !strcmp(argv[2], "--debug")) smtp_debug = 1; char *server = strtok(argv[1], ":"), *aport; short port; if ((aport = strtok(NULL, ""))) port = atoi(aport); else port = 25; if (!server) { alog("No Server"); /* Bad, bad, bad. This was a return from main with no value! -GD */ return 0; } else alog("SMTP: server %s port %d",server,port); /* The WSAStartup function initiates use of WS2_32.DLL by a process. */ /* guessing we can skip it under *nix */ #ifdef _WIN32 if (WSAStartup(MAKEWORD(1, 1), &wsa)) return 0; #endif char buf[8192]; bool headers_done = false; /* Read the message and parse it */ while (fgets(buf, 8192, stdin)) { if (smtp_is_header(buf) && !headers_done) { smail.smtp_headers.push_back(strip(buf) + "\r\n"); std::string header, value; smtp_parse_header(buf, header, value); if (header == "From:") { alog("SMTP: from: %s", value.c_str()); smail.from = value; } else if (header == "To:") { alog("SMTP: to: %s", value.c_str()); smtp_set_to(value); } else if (smtp_is_end(buf)) break; else { smail.smtp_headers.push_back("Date: " + GetTimeStamp() + "\r\n"); headers_done = true; smail.smtp_body.push_back(strip(buf) + "\r\n"); } } else smail.smtp_body.push_back(strip(buf) + "\r\n"); } if (!smtp_connect(server, port)) { alog("SMTP: failed to connect to %s:%d", server, port); return 0; } if (!smtp_send_email()) { alog("SMTP: error during sending of mail"); return 0; } smtp_disconnect(); return 1; }