anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

anopesmtp.cpp (10349B)

      1 /* smtp stuff handler for win32.
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  *
     11  * Written by Dominick Meglio <codemastr@unrealircd.com>
     12  * *nix port by Trystan Scott Lee <trystan@nomadirc.net>
     13  */
     14 
     15 #include "sysconf.h"
     16 
     17 /* Some Linux boxes (or maybe glibc includes) require this for the
     18  * prototype of strsignal(). */
     19 #ifndef _GNU_SOURCE
     20 # define _GNU_SOURCE
     21 #endif
     22 
     23 #include <string>
     24 #include <vector>
     25 #include <cstdarg>
     26 #include <cstdio>
     27 #include <cstdlib>
     28 #include <cstring>
     29 #include <ctime>
     30 #include <cerrno>
     31 #include <iostream>
     32 #include <fstream>
     33 
     34 #ifndef _WIN32
     35 # include <unistd.h>
     36 # include <netdb.h>
     37 # include <netinet/in.h>
     38 # include <sys/socket.h>
     39 # include <arpa/inet.h>
     40 # include <sys/time.h>
     41 #else
     42 # include <winsock.h>
     43 # define WIN32_LEAN_AND_MEAN
     44 # include <windows.h>
     45 #endif
     46 
     47 #include <sys/types.h>
     48 
     49 #ifdef _AIX
     50 extern int strcasecmp(const char *, const char *);
     51 extern int strncasecmp(const char *, const char *, size_t);
     52 # if 0 /* These break on some AIX boxes (4.3.1 reported). */
     53 extern int socket(int, int, int);
     54 extern int connect(int, struct sockaddr *, int);
     55 # endif
     56 #endif /* _AIX */
     57 
     58 /* Some SUN fixs */
     59 #ifdef __sun
     60 /* Solaris specific code, types that do not exist in Solaris'
     61  *  * sys/types.h
     62  *   **/
     63 # ifndef INADDR_NONE
     64 #  define INADDR_NONE (-1)
     65 # endif
     66 #endif
     67 
     68 #ifdef _WIN32
     69 typedef SOCKET ano_socket_t;
     70 #define ano_sockclose(fd) closesocket(fd)
     71 #define ano_sockread(fd, buf, len) recv(fd, buf, len, 0)
     72 #define ano_sockwrite(fd, buf, len) send(fd, buf, len, 0)
     73 #else
     74 typedef int ano_socket_t;
     75 #define ano_sockclose(fd) close(fd)
     76 #define ano_sockread(fd, buf, len) read(fd, buf, len)
     77 #define ano_sockwrite(fd, buf, len) write(fd, buf, len)
     78 #define SOCKET_ERROR -1
     79 #endif
     80 
     81 /* Data structures */
     82 struct smtp_message
     83 {
     84 	std::vector<std::string> smtp_headers;
     85 	std::vector<std::string> smtp_body;
     86 	std::string from;
     87 	std::string to;
     88 	ano_socket_t sock;
     89 };
     90 
     91 int smtp_debug = 0;
     92 
     93 struct smtp_message smail;
     94 
     95 static std::string get_logname(struct tm *tm = NULL)
     96 {
     97 	char timestamp[32];
     98 
     99 	if (!tm)
    100 	{
    101 		time_t t = time(NULL);
    102 		tm = localtime(&t);
    103 	}
    104 
    105 	strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm);
    106 	std::string name = std::string("anopesmtp.") + timestamp;
    107 	return name;
    108 }
    109 
    110 /* TimeStamp for Email Header */
    111 static std::string GetTimeStamp()
    112 {
    113 	char tbuf[256];
    114 	time_t t = time(NULL);
    115 	struct tm *tm = gmtime(&t);
    116 
    117 	strftime(tbuf, sizeof(tbuf) - 1, "%a, %d %b %Y %H:%M:%S +0000", tm);
    118 
    119 	return tbuf;
    120 }
    121 
    122 /* Log stuff to the log file with a datestamp.  Note that errno is
    123  * preserved by this routine and log_perror().
    124  */
    125 
    126 void alog(const char *fmt, ...)
    127 {
    128 	if (!smtp_debug || !fmt)
    129 		return;
    130 
    131 	std::fstream file;
    132 	file.open(get_logname().c_str(), std::ios_base::out | std::ios_base::app);
    133 
    134 	if (!file.is_open())
    135 		return;
    136 
    137 	va_list args;
    138 	va_start(args, fmt);
    139 
    140 	time_t t = time(NULL);
    141 	struct tm *tm = localtime(&t);
    142 
    143 	char buf[256];
    144 	strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", tm);
    145 	file << buf;
    146 	vsnprintf(buf, sizeof(buf), fmt, args);
    147 	file << buf << std::endl;
    148 	va_end(args);
    149 	va_end(args);
    150 
    151 	file.close();
    152 }
    153 
    154 /* Remove a trailing \r\n */
    155 std::string strip(const std::string &buf)
    156 {
    157 	std::string newbuf = buf;
    158 	char c = newbuf[newbuf.size() - 1];
    159 	while (c == '\n' || c == '\r')
    160 	{
    161 		newbuf.erase(newbuf.end() - 1);
    162 		c = newbuf[newbuf.size() - 1];
    163 	}
    164 	return newbuf;
    165 }
    166 
    167 /* Is the buffer a header? */
    168 bool smtp_is_header(const std::string &buf)
    169 {
    170 	size_t tmp = buf.find(' ');
    171 
    172 	if (tmp == std::string::npos)
    173 		return false;
    174 
    175 	if (tmp > 0 && buf[tmp - 1] == ':')
    176 		return true;
    177 	return false;
    178 }
    179 
    180 /* Parse a header into a name and value */
    181 void smtp_parse_header(const std::string &buf, std::string &header, std::string &value)
    182 {
    183 	std::string newbuf = strip(buf);
    184 
    185 	size_t space = newbuf.find(' ');
    186 	if (space != std::string::npos)
    187 	{
    188 		header = newbuf.substr(0, space);
    189 		value = newbuf.substr(space + 1);
    190 	}
    191 	else
    192 	{
    193 		header = newbuf;
    194 		value = "";
    195 	}
    196 }
    197 
    198 /* Have we reached the end of input? */
    199 bool smtp_is_end(const std::string &buf)
    200 {
    201 	if (buf[0] == '.')
    202 		if (buf[1] == '\r' || buf[1] == '\n')
    203 			return true;
    204 
    205 	return false;
    206 }
    207 
    208 /* Set who the email is to */
    209 void smtp_set_to(const std::string &to)
    210 {
    211 	smail.to = to;
    212 	size_t c = smail.to.rfind('<');
    213 	if (c != std::string::npos && c + 1 < smail.to.size())
    214 	{
    215 		smail.to = smail.to.substr(c + 1);
    216 		smail.to.erase(smail.to.end() - 1);
    217 	}
    218 }
    219 
    220 /* Establish a connection to the SMTP server */
    221 int smtp_connect(const char *host, unsigned short port)
    222 {
    223 	struct sockaddr_in addr;
    224 
    225 	if ((smail.sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
    226 		return 0;
    227 
    228 	if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
    229 	{
    230 		struct hostent *hent;
    231 		if (!(hent = gethostbyname(host)))
    232 			return 0;
    233 		memcpy(&addr.sin_addr, hent->h_addr, hent->h_length);
    234 	}
    235 	addr.sin_family = AF_INET;
    236 	addr.sin_port = htons(port ? port : 25);
    237 	if (connect(smail.sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(struct sockaddr_in)) == SOCKET_ERROR)
    238 	{
    239 		ano_sockclose(smail.sock);
    240 		return 0;
    241 	}
    242 
    243 	return 1;
    244 }
    245 
    246 /* Send a line of text */
    247 int smtp_send(const char *text)
    248 {
    249 	int result = ano_sockwrite(smail.sock, text, strlen(text));
    250 
    251 	alog("SMTP: sent %s",text);
    252 
    253 	if (result == SOCKET_ERROR)
    254 		ano_sockclose(smail.sock);
    255 
    256 	return result;
    257 }
    258 
    259 /* Read a line of text */
    260 int smtp_read(char *buf, int len)
    261 {
    262 	int result;
    263 
    264 	memset(buf, 0, len);
    265 	result = ano_sockread(smail.sock, buf, len);
    266 
    267 	if (result == SOCKET_ERROR)
    268 		ano_sockclose(smail.sock);
    269 
    270 	return result;
    271 }
    272 
    273 /* Retrieve a response code */
    274 int smtp_get_code(const std::string &text)
    275 {
    276 	size_t tmp = text.find(' ');
    277 
    278 	if (tmp == std::string::npos)
    279 		return 0;
    280 
    281 	return atol(text.substr(0, tmp).c_str());
    282 }
    283 
    284 /* Send the email */
    285 int smtp_send_email()
    286 {
    287 	char buf[1024];
    288 	if (!smtp_read(buf, 1024))
    289 	{
    290 		alog("SMTP: error reading buffer");
    291 		return 0;
    292 	}
    293 
    294 	int code = smtp_get_code(buf);
    295 	if (code != 220)
    296 	{
    297 		alog("SMTP: error expected code 220 got %d",code);
    298 		return 0;
    299 	}
    300 
    301 	if (!smtp_send("HELO anope\r\n"))
    302 	{
    303 		alog("SMTP: error writing to socket");
    304 		return 0;
    305 	}
    306 
    307 	if (!smtp_read(buf, 1024))
    308 	{
    309 		alog("SMTP: error reading buffer");
    310 		return 0;
    311 	}
    312 
    313 	code = smtp_get_code(buf);
    314 	if (code != 250)
    315 	{
    316 		alog("SMTP: error expected code 250 got %d",code);
    317 		return 0;
    318 	}
    319 
    320 	strcpy(buf, "MAIL FROM: <");
    321 	strcat(buf, smail.from.c_str());
    322 	strcat(buf, ">\r\n");
    323 
    324 	if (!smtp_send(buf))
    325 	{
    326 		alog("SMTP: error writing to socket");
    327 		return 0;
    328 	}
    329 
    330 	if (!smtp_read(buf, 1024))
    331 	{
    332 		alog("SMTP: error reading buffer");
    333 		return 0;
    334 	}
    335 
    336 	code = smtp_get_code(buf);
    337 	if (code != 250)
    338 		return 0;
    339 
    340 	strcpy(buf, "RCPT TO: <");
    341 	strcat(buf, smail.to.c_str());
    342 	strcat(buf, ">\r\n");
    343 
    344 	if (!smtp_send(buf))
    345 	{
    346 		alog("SMTP: error writing to socket");
    347 		return 0;
    348 	}
    349 
    350 	if (!smtp_read(buf, 1024))
    351 	{
    352 		alog("SMTP: error reading buffer");
    353 		return 0;
    354 	}
    355 
    356 	code = smtp_get_code(buf);
    357 	if (smtp_get_code(buf) != 250)
    358 	{
    359 		alog("SMTP: error expected code 250 got %d",code);
    360 		return 0;
    361 	}
    362 
    363 	if (!smtp_send("DATA\r\n"))
    364 	{
    365 		alog("SMTP: error writing to socket");
    366 		return 0;
    367 	}
    368 
    369 	if (!smtp_read(buf, 1024))
    370 	{
    371 		alog("SMTP: error reading buffer");
    372 		return 0;
    373 	}
    374 
    375 	code = smtp_get_code(buf);
    376 	if (code != 354)
    377 	{
    378 		alog("SMTP: error expected code 354 got %d",code);
    379 		return 0;
    380 	}
    381 
    382 	for (std::vector<std::string>::const_iterator it = smail.smtp_headers.begin(), it_end = smail.smtp_headers.end(); it != it_end; ++it)
    383 		if (!smtp_send(it->c_str()))
    384 		{
    385 			alog("SMTP: error writing to socket");
    386 			return 0;
    387 		}
    388 
    389 	if (!smtp_send("\r\n"))
    390 	{
    391 		alog("SMTP: error writing to socket");
    392 		return 0;
    393 	}
    394 
    395 	bool skip_done = false;
    396 	for (std::vector<std::string>::const_iterator it = smail.smtp_body.begin(), it_end = smail.smtp_body.end(); it != it_end; ++it)
    397 		if (skip_done)
    398 		{
    399 			if (!smtp_send(it->c_str()))
    400 			{
    401 				alog("SMTP: error writing to socket");
    402 				return 0;
    403 			}
    404 		}
    405 		else
    406 			skip_done = true;
    407 
    408 	if (!smtp_send("\r\n.\r\n"))
    409 	{
    410 		alog("SMTP: error writing to socket");
    411 		return 0;
    412 	}
    413 
    414 	if (!smtp_read(buf, 1024))
    415 	{
    416 		alog("SMTP: error reading buffer");
    417 		return 0;
    418 	}
    419 
    420 	code = smtp_get_code(buf);
    421 	if (code != 250)
    422 	{
    423 		alog("SMTP: error expected code 250 got %d",code);
    424 		return 0;
    425 	}
    426 
    427 	return 1;
    428 }
    429 
    430 void smtp_disconnect()
    431 {
    432 	char buf[1024];
    433 
    434 	if (!smtp_send("QUIT\r\n"))
    435 	{
    436 		alog("SMTP: error writing to socket");
    437 	}
    438 
    439 	if (!smtp_read(buf, 1024))
    440 	{
    441 		alog("SMTP: error reading buffer");
    442 	}
    443 
    444 	int code = smtp_get_code(buf);
    445 	if (code != 221)
    446 	{
    447 		alog("SMTP: error expected code 221 got %d",code);
    448 	}
    449 
    450 	ano_sockclose(smail.sock);
    451 }
    452 
    453 int main(int argc, char *argv[])
    454 {
    455 	/* Win32 stuff */
    456 #ifdef _WIN32
    457 	WSADATA wsa;
    458 #endif
    459 
    460 	if (argc == 1)
    461 		return 0;
    462 
    463 	if (argc == 3 && !strcmp(argv[2], "--debug"))
    464 		smtp_debug = 1;
    465 
    466 	char *server = strtok(argv[1], ":"), *aport;
    467 	short port;
    468 	if ((aport = strtok(NULL, "")))
    469 		port = atoi(aport);
    470 	else
    471 		port = 25;
    472 
    473 	if (!server)
    474 	{
    475 		alog("No Server");
    476 		/* Bad, bad, bad. This was a return from main with no value! -GD */
    477 		return 0;
    478 	}
    479 	else
    480 		alog("SMTP: server %s port %d",server,port);
    481 
    482 	/* The WSAStartup function initiates use of WS2_32.DLL by a process. */
    483 	/* guessing we can skip it under *nix */
    484 #ifdef _WIN32
    485 	if (WSAStartup(MAKEWORD(1, 1), &wsa))
    486 		return 0;
    487 #endif
    488 
    489 	char buf[8192];
    490 	bool headers_done = false;
    491 	/* Read the message and parse it */
    492 	while (fgets(buf, 8192, stdin))
    493 	{
    494 		if (smtp_is_header(buf) && !headers_done)
    495 		{
    496 			smail.smtp_headers.push_back(strip(buf) + "\r\n");
    497 			std::string header, value;
    498 			smtp_parse_header(buf, header, value);
    499 			if (header == "From:")
    500 			{
    501 				alog("SMTP: from: %s", value.c_str());
    502 				smail.from = value;
    503 			}
    504 			else if (header == "To:")
    505 			{
    506 				alog("SMTP: to: %s", value.c_str());
    507 				smtp_set_to(value);
    508 			}
    509 			else if (smtp_is_end(buf))
    510 				break;
    511 			else
    512 			{
    513 				smail.smtp_headers.push_back("Date: " + GetTimeStamp() + "\r\n");
    514 				headers_done = true;
    515 				smail.smtp_body.push_back(strip(buf) + "\r\n");
    516 			}
    517 		}
    518 		else
    519 			smail.smtp_body.push_back(strip(buf) + "\r\n");
    520 	}
    521 
    522 	if (!smtp_connect(server, port))
    523 	{
    524 		alog("SMTP: failed to connect to %s:%d", server, port);
    525 		return 0;
    526 	}
    527 	if (!smtp_send_email())
    528 	{
    529 		alog("SMTP: error during sending of mail");
    530 		return 0;
    531 	}
    532 	smtp_disconnect();
    533 
    534 	return 1;
    535 }