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