1 #include "smtp.h"
2 #include <unistd.h>
3 #include <netdb.h>
4 #include <arpa/inet.h>
5 #include <sys/socket.h>
6 #include <sys/time.h>
7 #include <time.h>
8 #include "userlist.h"
9 #include "logit.h"
10 #include "results.h"
11 #include <cstdlib>
12 #include <cstring>
13 
smtpData()14 smtpData::smtpData()
15  : m_quit("QUIT\r\n")
16  , m_randomLetters("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 `~!@#$%^&*()-_=+[]{};:'\"|/?<>,")
17  , m_randomLen(strlen(m_randomLetters))
18  , m_postalMsg("\r\nX-Postal: " VER_STR " - the mad postman.\r\n"
19                "X-Postal: http://www.coker.com.au/postal/\r\n"
20                "X-Postal: This is not a real email.\r\n\r\n")
21  , m_dnsLock(true)
22  , m_timeLastAction(time(NULL))
23 {
24   setRand(0);
25 }
26 
getMailName(struct sockaddr_in & in)27 const string *smtpData::getMailName(struct sockaddr_in &in)
28 {
29   Lock l(m_dnsLock);
30   unsigned long ip = in.sin_addr.s_addr;
31   string *name = m_names[ip];
32   if(name != NULL)
33     return name;
34   struct hostent *h;
35   h = gethostbyaddr((char *)&(in.sin_addr), sizeof(in.sin_addr), AF_INET);
36   if(!h)
37   {
38     FILE *fp = fopen("/etc/mailname", "r");
39     char buf[100];
40     if(fp && fgets(buf, sizeof(buf), fp))
41     {
42       char *tmp = strchr(buf, '\n');
43       if(tmp)
44         *tmp = 0;
45       name = new string(buf);
46     }
47     else
48     {
49       name = new string(inet_ntoa(in.sin_addr));
50     }
51   }
52   else
53   {
54     name = new string(h->h_name);
55   }
56   m_names[ip] = name;
57   return name;
58 }
59 
~smtpData()60 smtpData::~smtpData()
61 {
62 }
63 
setRand(int frequency)64 void smtpData::setRand(int frequency)
65 {
66   if(time(NULL) - m_timeLastAction < frequency)
67     return;
68 
69   for(int i = 0; i < MAP_SIZE; i++)
70     m_randBuf[i] = m_randomLetters[random() % m_randomLen];
71   m_timeLastAction = time(NULL);
72 }
73 
randomString(char * buf,int len) const74 void smtpData::randomString(char *buf, int len) const
75 {
76   if(len > 2)
77   {
78     int offset = random() % (MAP_SIZE - (len - 2));
79     memcpy(buf, &m_randBuf[offset], len - 2);
80   }
81   strcpy(buf + len - 2, "\r\n");
82 }
83 
84 const int max_line_len = 79;
85 
randomBuf(char * buf,int len) const86 void smtpData::randomBuf(char *buf, int len) const
87 {
88   while(len)
89   {
90     int line_len = random() % max_line_len;
91     if(line_len < 2)
92       line_len = 2;
93     if(len - line_len < 2)
94       line_len = len;
95     randomString(buf, line_len);
96     len -= line_len;
97     buf += line_len;
98   }
99 }
100 
101 // Return a random date that may be as much as 60 seconds in the future or 600 seconds in the past.
102 // buffer must be at least 34 bytes for "Day, dd Mon yyyy hh:mm:ss +zzzz\r\n"
date(char * buf) const103 void smtpData::date(char *buf) const
104 {
105   time_t t = time(NULL);
106   struct tm broken;
107 
108   t += 60 - random() % 600;
109 
110   if(!gmtime_r(&t, &broken) || !strftime(buf, 34, "%a, %d %b %Y %H:%M:%S %z\r\n", &broken))
111     strcpy(buf, "Error making date");
112 }
113 
msgId(const char * sender,const unsigned threadNum) const114 const string smtpData::msgId(const char *sender, const unsigned threadNum) const
115 {
116   char msgId_buf[256];
117   const unsigned int max_sender_len = sizeof(msgId_buf) - 35;
118 
119   if(strlen(sender) > max_sender_len)
120     sender += strlen(sender) - max_sender_len;
121   else if(*sender == '<')
122     sender++;
123 
124   struct timeval tv;
125   gettimeofday(&tv, NULL);
126   snprintf(msgId_buf, sizeof(msgId_buf), "<%08X.%03X.%03X.%s\r\n", unsigned(tv.tv_sec), unsigned(tv.tv_usec % 2048), threadNum % 2048, sender);
127   return string(msgId_buf);
128 }
129 
newThread(int threadNum)130 Thread *smtp::newThread(int threadNum)
131 {
132   return new smtp(threadNum, this);
133 }
134 
action(PVOID)135 int smtp::action(PVOID)
136 {
137   while(1)
138   {
139     int rc = Connect();
140     if(rc > 1)
141       return 1;
142     if(rc == 0)
143     {
144 #ifdef USE_SSL
145       if(m_canTLS && CHECK_PERCENT(m_useTLS) )
146       {
147         rc = sendCommandString("STARTTLS\r\n");
148         if(!rc)
149           rc = ConnectTLS();
150         if(!rc)
151           rc = sendCommandString(m_helo);
152         if(rc > 1)
153           return rc;
154         m_res->connect_ssl();
155       }
156 #endif
157       int msgs;
158       if(m_msgsPerConnection == 0)
159         msgs = -1;
160       else if(m_msgsPerConnection < 0)
161         msgs = 0;
162       else
163         msgs = random() % m_msgsPerConnection + 1;
164 
165       if(rc)
166         msgs = 0;
167       for(int i = 0; i != msgs; i++)
168       {
169         if(*m_exitCount)
170         {
171           disconnect();
172           return 1;
173         }
174         rc = sendMsg();
175         if(rc > 1)
176           return 1;
177         if(rc)
178           break;
179       }
180       if(!rc)
181         rc = disconnect();
182       if(rc > 1)
183         return 1;
184     }
185     if(rc)
186     {
187       sleep(5);
188     }
189   }
190 }
191 
smtp(int * exitCount,const char * addr,const char * ourAddr,UserList & ul,UserList * senderList,int minMsgSize,int maxMsgSize,int numMsgsPerConnection,int processes,Logit * log,TRISTATE netscape,bool useLMTP,int ssl,unsigned short port,Logit * debug)192 smtp::smtp(int *exitCount, const char *addr, const char *ourAddr, UserList &ul
193          , UserList *senderList, int minMsgSize, int maxMsgSize
194          , int numMsgsPerConnection
195          , int processes, Logit *log, TRISTATE netscape, bool useLMTP
196 #ifdef USE_SSL
197          , int ssl
198 #endif
199          , unsigned short port, Logit *debug)
200  : tcp(exitCount, addr, port, log
201 #ifdef USE_SSL
202      , ssl
203 #endif
204      , ourAddr, debug)
205  , m_ul(ul)
206  , m_senderList(senderList ? senderList : &ul)
207  , m_minMsgSize(minMsgSize * 1024)
208  , m_maxMsgSize(maxMsgSize * 1024)
209  , m_data(new smtpData())
210  , m_msgsPerConnection(numMsgsPerConnection)
211  , m_res(new results)
212  , m_netscape(netscape)
213  , m_nextPrint(time(NULL)/60*60+60)
214  , m_useLMTP(useLMTP)
215 {
216   go(NULL, processes);
217 }
218 
smtp(int threadNum,const smtp * parent)219 smtp::smtp(int threadNum, const smtp *parent)
220  : tcp(threadNum, parent)
221  , m_ul(parent->m_ul)
222  , m_senderList(parent->m_senderList)
223  , m_minMsgSize(parent->m_minMsgSize)
224  , m_maxMsgSize(parent->m_maxMsgSize)
225  , m_data(parent->m_data)
226  , m_msgsPerConnection(parent->m_msgsPerConnection)
227  , m_res(parent->m_res)
228  , m_netscape(parent->m_netscape)
229  , m_nextPrint(0)
230  , m_useLMTP(parent->m_useLMTP)
231 {
232 }
233 
~smtp()234 smtp::~smtp()
235 {
236   if(getThreadNum() < 1)
237     delete m_data;
238 }
239 
sentData(int bytes)240 void smtp::sentData(int bytes)
241 {
242   m_res->dataBytes(bytes);
243 }
244 
receivedData(int)245 void smtp::receivedData(int)
246 {
247 }
248 
error()249 void smtp::error()
250 {
251   m_res->error();
252   disconnect();
253 }
254 
Connect()255 int smtp::Connect()
256 {
257   int rc = tcp::Connect();
258   if(rc)
259     return rc;
260   m_res->connection();
261   rc = readCommandResp();
262   if(rc)
263     return rc;
264   const string *mailName = m_data->getMailName(m_connectionLocalAddr);
265   if(m_useLMTP)
266     m_helo = string("LHLO ") + *mailName + "\r\n";
267   else
268     m_helo = string("EHLO ") + *mailName + "\r\n";
269   rc = sendCommandString(m_helo);
270   if(rc)
271     return rc;
272   return 0;
273 }
274 
disconnect()275 int smtp::disconnect()
276 {
277   int rc = sendCommandString(m_data->quit());
278   rc |= tcp::disconnect();
279   return rc;
280 }
281 
sendMsg()282 int smtp::sendMsg()
283 {
284   int rc;
285   int size = 0;
286   if(m_maxMsgSize > m_minMsgSize)
287     size = random() % (m_maxMsgSize - m_minMsgSize) + m_minMsgSize;
288   else
289     size = m_maxMsgSize;
290   m_md5.init();
291   string logData;
292   bool logAll = false;
293   if(m_log && m_log->verbose())
294     logAll = true;
295 
296   char aByte;
297   rc = Read(&aByte, 1, 0);
298   if(rc != 1)
299     return 1;
300 
301   string sender = m_senderList->randomUser();
302   string from = string("<") + sender + '>';
303   string to = string("<") + m_ul.randomUser() + '>';
304   rc = sendCommandString(string("MAIL FROM: ") + from + "\r\n");
305   if(rc)
306     return rc;
307   rc = sendCommandString(string("RCPT TO: ") + to + "\r\n");
308   if(rc)
309     return rc;
310   rc = sendCommandString("DATA\r\n");
311   if(rc)
312     return rc;
313   string subject = string("Subject: ");
314   if(m_netscape == eWONT)
315     subject += "N";
316   else if(m_netscape == eMUST)
317     subject += " ";
318   char subj_buf[61];
319   // random string from 2 to buffer len
320   int subj_len = random() % (sizeof(subj_buf) - 3) + 2;
321   m_data->randomString(subj_buf, subj_len);
322   subj_buf[subj_len] = 0;
323   subject += subj_buf;
324   char date[34];
325   m_data->date(date);
326   string msgId = m_data->msgId(from.c_str(), getThreadNum());
327   string str = string("From: ") + from + "\r\nTo: " + to + "\r\n"
328                 + subject + "Date: " + date + "Message-Id: " + msgId;
329   m_md5.addData(str);
330 
331   char *sendbuf = (char *)malloc(size + 1);
332   m_data->randomBuf(sendbuf, size);
333   m_md5.addData(sendbuf, size);
334   string sum = m_md5.getSum();
335   str += "X-PostalHash: " + sum + m_data->postalMsg();
336   rc = sendString(str);
337   if(rc)
338   {
339     free(sendbuf);
340     return rc;
341   }
342   if(logAll)
343     logData = str;
344 
345   rc = sendData(sendbuf, size);
346   if(rc)
347     return rc;
348   if(logAll)
349     logData += sendbuf;
350   free(sendbuf);
351   str = ".\r\n";
352   rc = sendCommandString(str);
353   if(rc)
354     return rc;
355   m_res->message();
356   if(logAll)
357   {
358     logData += str;
359     m_log->Write(logData);
360   }
361   else if(m_log)
362   {
363     sum += " " + from + " " + to + "\n";
364     m_log->Write(sum);
365   }
366   return 0;
367 }
368 
readCommandResp()369 ERROR_TYPE smtp::readCommandResp()
370 {
371   char recvBuf[1024];
372   do
373   {
374     int rc = readLine(recvBuf, sizeof(recvBuf));
375     if(rc < 0)
376       return ERROR_TYPE(rc);
377     if(recvBuf[0] != '2' && recvBuf[0] != '3')
378     {
379       fprintf(stderr, "Server error:%s.\n", recvBuf);
380       error();
381       return eServer;
382     }
383 #ifdef USE_SSL
384     if(!m_canTLS)
385     {
386       if(!strncmp("250-STARTTLS", recvBuf, 12)
387          || !strncmp("250 STARTTLS", recvBuf, 12))
388       {
389         m_canTLS = true;
390       }
391     }
392 #endif
393   }
394   while(recvBuf[3] == '-');
395   return eNoError;
396 }
397 
pollRead()398 int smtp::pollRead()
399 {
400   m_data->setRand(RAND_TIME);
401   if(time(NULL) >= m_nextPrint)
402   {
403     m_res->print();
404     m_nextPrint += 60;
405   }
406   return 0;
407 }
408 
WriteWork(PVOID buf,int size,int timeout)409 int smtp::WriteWork(PVOID buf, int size, int timeout)
410 {
411   int t1 = time(NULL);
412   if(t1 + timeout > m_nextPrint)
413     timeout = m_nextPrint - t1;
414   if(timeout < 0)
415     timeout = 0;
416   int rc = Write(buf, size, timeout);
417   int t2 = time(NULL);
418   if(t2 < t1 + timeout)
419   {
420     struct timespec req;
421     req.tv_sec = t1 + timeout - t2;
422     req.tv_nsec = 0;
423     nanosleep(&req, NULL);
424   }
425   return rc;
426 }
427