Anope IRC Services  Version 2.0
anopesmtp.cpp
Go to the documentation of this file.
1 /* smtp stuff handler for win32.
2  *
3  * (C) 2003-2014 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 */
83 {
84  std::vector<std::string> smtp_headers;
85  std::vector<std::string> smtp_body;
89 };
90 
91 int smtp_debug = 0;
92 
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 /* Log stuff to the log file with a datestamp. Note that errno is
111  * preserved by this routine and log_perror().
112  */
113 
114 void alog(const char *fmt, ...)
115 {
116  if (!smtp_debug || !fmt)
117  return;
118 
119  std::fstream file;
120  file.open(get_logname().c_str(), std::ios_base::out | std::ios_base::app);
121 
122  if (!file.is_open())
123  return;
124 
125  va_list args;
126  va_start(args, fmt);
127 
128  time_t t = time(NULL);
129  struct tm *tm = localtime(&t);
130 
131  char buf[256];
132  strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", tm);
133  file << buf;
134  vsnprintf(buf, sizeof(buf), fmt, args);
135  file << buf << std::endl;
136  va_end(args);
137  va_end(args);
138 
139  file.close();
140 }
141 
142 /* Remove a trailing \r\n */
144 {
145  std::string newbuf = buf;
146  char c = newbuf[newbuf.size() - 1];
147  while (c == '\n' || c == '\r')
148  {
149  newbuf.erase(newbuf.end() - 1);
150  c = newbuf[newbuf.size() - 1];
151  }
152  return newbuf;
153 }
154 
155 /* Is the buffer a header? */
156 bool smtp_is_header(const std::string &buf)
157 {
158  size_t tmp = buf.find(' ');
159 
160  if (tmp == std::string::npos)
161  return false;
162 
163  if (tmp > 0 && buf[tmp - 1] == ':')
164  return true;
165  return false;
166 }
167 
168 /* Parse a header into a name and value */
169 void smtp_parse_header(const std::string &buf, std::string &header, std::string &value)
170 {
171  std::string newbuf = strip(buf);
172 
173  size_t space = newbuf.find(' ');
174  if (space != std::string::npos)
175  {
176  header = newbuf.substr(0, space);
177  value = newbuf.substr(space + 1);
178  }
179  else
180  {
181  header = newbuf;
182  value = "";
183  }
184 }
185 
186 /* Have we reached the end of input? */
187 bool smtp_is_end(const std::string &buf)
188 {
189  if (buf[0] == '.')
190  if (buf[1] == '\r' || buf[1] == '\n')
191  return true;
192 
193  return false;
194 }
195 
196 /* Set who the email is to */
197 void smtp_set_to(const std::string &to)
198 {
199  smail.to = to;
200  size_t c = smail.to.rfind('<');
201  if (c != std::string::npos && c + 1 < smail.to.size())
202  {
203  smail.to = smail.to.substr(c + 1);
204  smail.to.erase(smail.to.end() - 1);
205  }
206 }
207 
208 /* Establish a connection to the SMTP server */
209 int smtp_connect(const char *host, unsigned short port)
210 {
211  struct sockaddr_in addr;
212 
213  if ((smail.sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
214  return 0;
215 
216  if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
217  {
218  struct hostent *hent;
219  if (!(hent = gethostbyname(host)))
220  return 0;
221  memcpy(&addr.sin_addr, hent->h_addr, hent->h_length);
222  }
223  addr.sin_family = AF_INET;
224  addr.sin_port = htons(port ? port : 25);
225  if (connect(smail.sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(struct sockaddr_in)) == SOCKET_ERROR)
226  {
228  return 0;
229  }
230 
231  return 1;
232 }
233 
234 /* Send a line of text */
235 int smtp_send(const char *text)
236 {
237  int result = ano_sockwrite(smail.sock, text, strlen(text));
238 
239  alog("SMTP: sent %s",text);
240 
241  if (result == SOCKET_ERROR)
243 
244  return result;
245 }
246 
247 /* Read a line of text */
248 int smtp_read(char *buf, int len)
249 {
250  int result;
251 
252  memset(buf, 0, len);
253  result = ano_sockread(smail.sock, buf, len);
254 
255  if (result == SOCKET_ERROR)
257 
258  return result;
259 }
260 
261 /* Retrieve a response code */
262 int smtp_get_code(const std::string &text)
263 {
264  size_t tmp = text.find(' ');
265 
266  if (tmp == std::string::npos)
267  return 0;
268 
269  return atol(text.substr(0, tmp).c_str());
270 }
271 
272 /* Send the email */
274 {
275  char buf[1024];
276  if (!smtp_read(buf, 1024))
277  {
278  alog("SMTP: error reading buffer");
279  return 0;
280  }
281 
282  int code = smtp_get_code(buf);
283  if (code != 220)
284  {
285  alog("SMTP: error expected code 220 got %d",code);
286  return 0;
287  }
288 
289  if (!smtp_send("HELO anope\r\n"))
290  {
291  alog("SMTP: error writting to socket");
292  return 0;
293  }
294 
295  if (!smtp_read(buf, 1024))
296  {
297  alog("SMTP: error reading buffer");
298  return 0;
299  }
300 
301  code = smtp_get_code(buf);
302  if (code != 250)
303  {
304  alog("SMTP: error expected code 250 got %d",code);
305  return 0;
306  }
307 
308  strcpy(buf, "MAIL FROM: <");
309  strcat(buf, smail.from.c_str());
310  strcat(buf, ">\r\n");
311 
312  if (!smtp_send(buf))
313  {
314  alog("SMTP: error writting to socket");
315  return 0;
316  }
317 
318  if (!smtp_read(buf, 1024))
319  {
320  alog("SMTP: error reading buffer");
321  return 0;
322  }
323 
324  code = smtp_get_code(buf);
325  if (code != 250)
326  return 0;
327 
328  strcpy(buf, "RCPT TO: <");
329  strcat(buf, smail.to.c_str());
330  strcat(buf, ">\r\n");
331 
332  if (!smtp_send(buf))
333  {
334  alog("SMTP: error writting to socket");
335  return 0;
336  }
337 
338  if (!smtp_read(buf, 1024))
339  {
340  alog("SMTP: error reading buffer");
341  return 0;
342  }
343 
344  code = smtp_get_code(buf);
345  if (smtp_get_code(buf) != 250)
346  {
347  alog("SMTP: error expected code 250 got %d",code);
348  return 0;
349  }
350 
351  if (!smtp_send("DATA\r\n"))
352  {
353  alog("SMTP: error writting to socket");
354  return 0;
355  }
356 
357  if (!smtp_read(buf, 1024))
358  {
359  alog("SMTP: error reading buffer");
360  return 0;
361  }
362 
363  code = smtp_get_code(buf);
364  if (code != 354)
365  {
366  alog("SMTP: error expected code 354 got %d",code);
367  return 0;
368  }
369 
370  for (std::vector<std::string>::const_iterator it = smail.smtp_headers.begin(), it_end = smail.smtp_headers.end(); it != it_end; ++it)
371  if (!smtp_send(it->c_str()))
372  {
373  alog("SMTP: error writting to socket");
374  return 0;
375  }
376 
377  if (!smtp_send("\r\n"))
378  {
379  alog("SMTP: error writting to socket");
380  return 0;
381  }
382 
383  bool skip_done = false;
384  for (std::vector<std::string>::const_iterator it = smail.smtp_body.begin(), it_end = smail.smtp_body.end(); it != it_end; ++it)
385  if (skip_done)
386  {
387  if (!smtp_send(it->c_str()))
388  {
389  alog("SMTP: error writting to socket");
390  return 0;
391  }
392  }
393  else
394  skip_done = true;
395 
396  if (!smtp_send("\r\n.\r\n"))
397  {
398  alog("SMTP: error writting to socket");
399  return 0;
400  }
401 
402  return 1;
403 }
404 
406 {
407  smtp_send("QUIT\r\n");
409 }
410 
411 int main(int argc, char *argv[])
412 {
413  /* Win32 stuff */
414 #ifdef _WIN32
415  WSADATA wsa;
416 #endif
417 
418  if (argc == 1)
419  return 0;
420 
421  if (argc == 3 && !strcmp(argv[2], "--debug"))
422  smtp_debug = 1;
423 
424  char *server = strtok(argv[1], ":"), *aport;
425  short port;
426  if ((aport = strtok(NULL, "")))
427  port = atoi(aport);
428  else
429  port = 25;
430 
431  if (!server)
432  {
433  alog("No Server");
434  /* Bad, bad, bad. This was a return from main with no value! -GD */
435  return 0;
436  }
437  else
438  alog("SMTP: server %s port %d",server,port);
439 
440  /* The WSAStartup function initiates use of WS2_32.DLL by a process. */
441  /* guessing we can skip it under *nix */
442 #ifdef _WIN32
443  if (WSAStartup(MAKEWORD(1, 1), &wsa))
444  return 0;
445 #endif
446 
447  char buf[8192];
448  bool headers_done = false;
449  /* Read the message and parse it */
450  while (fgets(buf, 8192, stdin))
451  {
452  if (smtp_is_header(buf) && !headers_done)
453  {
454  smail.smtp_headers.push_back(strip(buf) + "\r\n");
455  std::string header, value;
456  smtp_parse_header(buf, header, value);
457  if (header == "From:")
458  {
459  alog("SMTP: from: %s", value.c_str());
460  smail.from = value;
461  }
462  else if (header == "To:")
463  {
464  alog("SMTP: to: %s", value.c_str());
465  smtp_set_to(value);
466  }
467  else if (smtp_is_end(buf))
468  break;
469  else
470  {
471  headers_done = true;
472  smail.smtp_body.push_back(strip(buf) + "\r\n");
473  }
474  }
475  else
476  smail.smtp_body.push_back(strip(buf) + "\r\n");
477  }
478 
479  if (!smtp_connect(server, port))
480  {
481  alog("SMTP: failed to connect to %s:%d", server, port);
482  return 0;
483  }
484  if (!smtp_send_email())
485  {
486  alog("SMTP: error during sending of mail");
487  return 0;
488  }
489  smtp_disconnect();
490 
491  return 1;
492 }
std::string from
Definition: anopesmtp.cpp:86
struct smtp_message smail
Definition: anopesmtp.cpp:93
#define SOCKET_ERROR
Definition: anopesmtp.cpp:78
#define ano_sockread(fd, buf, len)
Definition: anopesmtp.cpp:76
int smtp_debug
Definition: anopesmtp.cpp:91
bool smtp_is_header(const std::string &buf)
Definition: anopesmtp.cpp:156
ano_socket_t sock
Definition: anopesmtp.cpp:88
int smtp_send_email()
Definition: anopesmtp.cpp:273
void smtp_set_to(const std::string &to)
Definition: anopesmtp.cpp:197
std::string strip(const std::string &buf)
Definition: anopesmtp.cpp:143
void alog(const char *fmt,...)
Definition: anopesmtp.cpp:114
std::vector< std::string > smtp_body
Definition: anopesmtp.cpp:85
int main(int argc, char *argv[])
Definition: anopesmtp.cpp:411
std::vector< std::string > smtp_headers
Definition: anopesmtp.cpp:84
#define ano_sockclose(fd)
Definition: anopesmtp.cpp:75
std::basic_string< char, ci_char_traits, std::allocator< char > > string
Definition: hashcomp.h:133
std::string to
Definition: anopesmtp.cpp:87
#define ano_sockwrite(fd, buf, len)
Definition: anopesmtp.cpp:77
int smtp_get_code(const std::string &text)
Definition: anopesmtp.cpp:262
static std::string get_logname(struct tm *tm=NULL)
Definition: anopesmtp.cpp:95
int smtp_connect(const char *host, unsigned short port)
Definition: anopesmtp.cpp:209
int ano_socket_t
Definition: anopesmtp.cpp:74
bool smtp_is_end(const std::string &buf)
Definition: anopesmtp.cpp:187
Anope::string name
Definition: access.cpp:22
int smtp_read(char *buf, int len)
Definition: anopesmtp.cpp:248
void smtp_disconnect()
Definition: anopesmtp.cpp:405
int smtp_send(const char *text)
Definition: anopesmtp.cpp:235
CoreExport Anope::string strftime(time_t t, const NickCore *nc=NULL, bool short_output=false)
Definition: misc.cpp:356
void smtp_parse_header(const std::string &buf, std::string &header, std::string &value)
Definition: anopesmtp.cpp:169