"Fossies" - the Fresh Open Source Software Archive 
Member "postal-0.76/bhm.cpp" (30 Jun 2016, 11319 Bytes) of package /linux/privat/postal-0.76.tgz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "bhm.cpp" see the
Fossies "Dox" file reference documentation.
1 #ifndef _GNU_SOURCE
2 #define _GNU_SOURCE
3 #endif
4
5 #include "bhmusers.h"
6 #include <errno.h>
7 #include <ctype.h>
8 #include <unistd.h>
9 #include <sys/wait.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <sys/poll.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <arpa/inet.h>
16 #include <netinet/ip.h>
17
18 #include "postal.h"
19 #include "logit.h"
20 #include "results.h"
21 #include "basictcp.h"
22 #ifdef USE_GNUTLS
23 #include <errno.h>
24 #if GNUTLS_VERSION_NUMBER <= 0x020b00
25 #include <gcrypt.h>
26 GCRY_THREAD_OPTION_PTHREAD_IMPL;
27 #endif /* GNUTLS_VERSION_NUMBER */
28 #endif
29
30 int processes = 0;
31 int *thread_status;
32
33 void usage(CPCCHAR msg = NULL)
34 {
35 if(msg)
36 printf("Error: %s\n\n", msg);
37
38 printf("Usage: bhm [-m maximum-message-size] [-t threads] [-p port]"
39 #ifdef USE_SSL
40 " [-s]"
41 #endif
42 "\n"
43 " [-a] [-(z|Z) debug-file] [-r reverse-dns] [-b bind address ]\n"
44 " user-list-filename\n"
45 "\n"
46 "Postal Version: " VER_STR "\n");
47 close(1);
48 exit(eParam);
49 }
50
51 int maxMsgSize = 10240;
52 results res;
53 Logit *log;
54
55 int exitCount = 0;
56
57 void endit(int)
58 {
59 exitCount++;
60 if(exitCount > 2)
61 exit(1);
62 }
63
64 typedef struct
65 {
66 int threadNum;
67 int fd;
68 struct sockaddr_in addr;
69 int ssl;
70 Logit *debug;
71 } thread_data;
72
73 enum { eFree = 0, eUsed, eFinished };
74
75 int check_sender(CPCCHAR addr)
76 {
77 return 0;
78 }
79
80 int readCommand(base_tcp &t, char *buf, int bufSize, bool stripCR, int timeout = 60);
81
82 // return 1 for successfully accepting an address, 0 for quit, <0 for error
83 // return 2 for data
84 int recv_addr(base_tcp &t, char *buf, const size_t buf_len, CPCCHAR msg, string &addr, bool do_data)
85 {
86 while(1)
87 {
88 char *tmp;
89 bool syntax_warn = false;
90 int len = readCommand(t, buf, buf_len, true);
91 if(len < 0)
92 return len;
93 if(!strncasecmp(buf, "quit", 4) && (isblank(buf[4]) || buf[4] == '\0'))
94 return 0;
95 if(do_data && !strncasecmp(buf, "data", 4) && (len == 4 || (isblank(buf[4]) && len == 5)))
96 return 2;
97 if(!strncasecmp(buf, msg, strlen(msg)))
98 {
99 CPCCHAR bad_sender_msg = "501 Bad address syntax\r\n";
100 if(buf[len - 1] == '\r')
101 {
102 len--;
103 buf[len] = '\0';
104 }
105 tmp = buf + strlen(msg);
106 while(isblank(*tmp))
107 tmp++;
108 if(*tmp != '<' && buf[len - 1] != '>')
109 {
110 syntax_warn = true;
111 }
112 else if(*tmp != '<' || buf[len - 1] != '>')
113 {
114 t.sendCstr(bad_sender_msg);
115 res.error();
116 continue;
117 }
118 else
119 {
120 tmp++;
121 len--;
122 buf[len] = '\0';
123 }
124 if(check_sender(tmp))
125 {
126 t.sendCstr(bad_sender_msg);
127 res.error();
128 continue;
129 }
130 addr = tmp;
131 if(syntax_warn)
132 t.sendCstr("250 Ok but you should use <addr>\r\n");
133 else
134 t.sendCstr("250 Ok\r\n");
135 break;
136 }
137 res.error();
138 int rc = t.sendCstr("502 Error: Command not recognised or not appropriate at this time\r\n");
139 if(rc < 0)
140 return rc;
141 }
142 return 1;
143 }
144
145 // return 1 for successfully accepting a message, 0 for quit, -1 for error
146 int recv_msg(base_tcp &t, char *buf, const size_t buf_len)
147 {
148 string sender;
149 string recipient;
150 int rc = recv_addr(t, buf, buf_len, "mail from:", sender, false);
151 if(rc != 1)
152 return rc;
153
154 int recipient_count = 0;
155 while( (rc = recv_addr(t, buf, buf_len, "rcpt to:", recipient, recipient_count)) == 1)
156 {
157 recipient_count++;
158 }
159 if(rc <= 0)
160 return rc;
161 t.sendCstr("354 End data with <CR><LF>.<CR><LF>\r\n");
162 while( (rc = readCommand(t, buf, buf_len, true)) >= 0)
163 {
164 if(!strcmp(".", buf))
165 break;
166 }
167 if(rc < 0)
168 {
169 res.error();
170 return rc;
171 }
172 struct timespec req;
173 req.tv_sec = 0;
174 req.tv_nsec = 1000000;
175 nanosleep(&req, NULL);
176 t.sendCstr("250 Ok: queued on /dev/null\r\n");
177 res.message();
178 return 1;
179 }
180
181 const char *hostname = "bhm.coker.com.au";
182
183 int do_helo(base_tcp &t, char *buf, const size_t buf_len)
184 {
185 int len;
186 while(1)
187 {
188 int rc;
189 if( (len = t.readLine(buf, buf_len, true)) < 0)
190 {
191 res.error();
192 return -1;
193 }
194 if(buf[len - 1] == '\r')
195 {
196 len--;
197 buf[len] = '\0';
198 }
199 bool basicHello = false;
200 if(!strncasecmp("quit", buf, 4))
201 {
202 t.sendCstr("221 Bye\r\n");
203 return -2;
204 }
205 if(!strncasecmp("helo", buf, 4))
206 {
207 basicHello = true;
208 }
209 else if(strncasecmp("ehlo", buf, 4))
210 {
211 res.error();
212 if(t.sendCstr("502 Error: command not recognized\r\n") < 0)
213 return -3;
214 continue;
215 }
216 if(len < 6 || !isblank(buf[4]))
217 {
218 res.error();
219 if(t.sendCstr("501 Syntax: EHLO hostname or HELO hostname\r\n") < 0)
220 return -3;
221 continue;
222 }
223 string remoteName(buf + 5);
224 if(basicHello)
225 rc = t.printf("250 %s\r\n", hostname);
226 else
227 rc = t.printf("250-%s\r\n250-PIPELINING\r\n"
228 #ifdef USE_SSL
229 "250-STARTTLS\r\n"
230 #endif
231 "250 SIZE %d\r\n", hostname, maxMsgSize * 1024);
232 if(rc < 0)
233 {
234 res.error();
235 return -1;
236 }
237 break;
238 }
239 return 0;
240 }
241
242 int readCommand(base_tcp &t, char *buf, int bufSize, bool stripCR, int timeout)
243 {
244 #ifdef USE_SSL
245 if(!t.isTLS())
246 {
247 int rc = t.readLine(buf, bufSize, stripCR, timeout);
248 if(rc < 0 || strcasecmp(buf, "starttls"))
249 return rc;
250 t.printf("220 2.0.0 Ready to start TLS\r\n");
251 if(t.ConnectTLS())
252 return -1;
253 res.connect_ssl();
254 rc = do_helo(t, buf, bufSize);
255 if(rc < 0)
256 return rc; // need to make sure values are consistent
257 rc = t.readLine(buf, bufSize, stripCR, timeout);
258 return rc;
259 }
260 else
261 #endif
262 return t.readLine(buf, bufSize, stripCR, timeout);
263 }
264
265 void do_work(thread_data *td)
266 {
267 base_tcp t(td->fd, log, td->debug, &res
268 #ifdef USE_SSL
269 , td->ssl
270 #endif
271 );
272 struct sockaddr_in name;
273 socklen_t namelen = sizeof(name);
274 char buf[1024];
275
276 int rc = getsockname(td->fd, (sockaddr *)&name, &namelen);
277
278 if(rc)
279 {
280 fprintf(stderr, "Error getting socket name.\n");
281 return;
282 }
283 /* if(!inet_ntop(name.sin_family, &name.sin_addr, buf, sizeof(buf)))
284 printf("error decoding\n");
285 else
286 printf("Addr:%s\n", buf);
287 */
288 t.printf("220 %s ESMTP BHM\r\n", hostname);
289 do_helo(t, buf, sizeof(buf));
290 while( (rc = recv_msg(t, buf, sizeof(buf))) == 1)
291 { }
292 if(rc == eTimeout)
293 t.printf("421 %s Error: timeout exceeded\r\n", hostname);
294 if(rc < 0)
295 {
296 res.error();
297 return;
298 }
299 if(rc == 0)
300 t.sendCstr("221 2.0.0 Bye\r\n");
301 }
302
303 PVOID smtp_worker(PVOID param)
304 {
305 thread_data *td = (thread_data *)param;
306 do_work(td);
307 if(td->debug)
308 delete td->debug;
309 thread_status[td->threadNum] = eFinished;
310 close(td->fd);
311 free(td);
312 return 0;
313 }
314
315 int main(int argc, char **argv)
316 {
317 int maxThreads = 100;
318 bool allLog = false;
319 PCCHAR debugName = NULL;
320 bool debugMultipleFiles = false;
321 #ifdef USE_SSL
322 int use_ssl = false;
323 #endif
324 unsigned short port = 25;
325 struct in_addr sin_addr;
326 sin_addr.s_addr = INADDR_ANY;
327 #ifdef USE_GNUTLS
328 #if GNUTLS_VERSION_NUMBER <= 0x020b00
329 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
330 #endif
331 gnutls_global_init();
332 if(!gnutls_check_version(GNUTLS_VER))
333 {
334 fprintf(stderr, "Needs version " GNUTLS_VER " of GNUTLS\n");
335 exit(1);
336 }
337 #endif
338
339 int c;
340 while(-1 != (c = getopt(argc, argv, "b:m:p:st:z:Z:")) )
341 {
342 switch(char(c))
343 {
344 case '?':
345 case ':':
346 usage();
347 break;
348 case 'a':
349 allLog = true;
350 break;
351 case 'b':
352 if(!inet_aton(optarg, &sin_addr))
353 {
354 fprintf(stderr, "Invalid bind address \"%s\"\n", optarg);
355 return 1;
356 }
357 break;
358 case 'm':
359 maxMsgSize = atoi(optarg);
360 break;
361 case 'p':
362 port = atoi(optarg);
363 break;
364 case 's':
365 #ifdef USE_SSL
366 use_ssl = true;
367 #else
368 usage("SSL not supported.\n");
369 #endif
370 break;
371 case 't':
372 maxThreads = atoi(optarg);
373 break;
374 case 'Z':
375 debugMultipleFiles = true;
376 case 'z':
377 debugName = optarg;
378 break;
379 }
380 }
381 if(maxMsgSize < 0 || maxMsgSize > MAX_MSG_SIZE)
382 usage("Bad maximum message size.\n");
383
384 if(maxThreads < 0 || maxThreads > MAX_PROCESSES)
385 usage("Bad maximum process limit.\n");
386
387 if(optind + 1 != argc)
388 usage();
389
390 BHMUsers user_list(argv[optind]);
391
392 struct sigaction sa;
393 memset(&sa, 0, sizeof(sa));
394 sa.sa_sigaction = NULL;
395 sa.sa_handler = SIG_IGN;
396 sa.sa_flags = 0;
397 if(sigaction(SIGPIPE, &sa, NULL))
398 {
399 printf("Can't block SIGPIPE.\n");
400 return eSystem;
401 }
402
403 sa.sa_flags = SA_SIGINFO;
404 sa.sa_handler = &endit;
405 if(sigaction(SIGINT, &sa, NULL))
406 {
407 printf("Can't handle SIGINT.\n");
408 return eSystem;
409 }
410
411 printf("time,messages,data(K),errors,connections"
412 #ifdef USE_SSL
413 ",SSL connections"
414 #endif
415 "\n");
416
417 log = new Logit("bhm.log", allLog, false, 0);
418 Logit *debug = NULL;
419
420 if(debugName)
421 debug = new Logit(debugName, false, debugMultipleFiles, 0);
422
423 int listen_fd = socket(PF_INET, SOCK_STREAM, 0);
424 struct sockaddr_in in;
425 in.sin_family = AF_INET;
426 in.sin_port = htons(port);
427 memcpy(&in.sin_addr, &sin_addr, sizeof(sin_addr));
428 if(listen_fd == -1 || bind(listen_fd, (sockaddr *)&in, sizeof(in))
429 || listen(listen_fd, 10))
430 {
431 fprintf(stderr, "Can't bind to port.\n");
432 return 1;
433 }
434 struct pollfd pfd;
435 pfd.fd = listen_fd;
436 pfd.events = POLLIN;
437 pthread_t *thread_info = (pthread_t *)calloc(sizeof(thread_info), maxThreads);
438 thread_status = (int *)calloc(sizeof(int), maxThreads);
439 time_t nextPrint = time(NULL)/60*60+60;
440 while(1)
441 {
442 time_t t = time(NULL);
443 if(t >= nextPrint)
444 {
445 res.print();
446 nextPrint += 60;
447 }
448 int timeout = (nextPrint - t) * 1000;
449 int rc = poll(&pfd, 1, timeout);
450 if(rc < 0)
451 {
452 if(errno == EINTR)
453 continue;
454 fprintf(stderr, "Poll error on accept.\n");
455 return 1;
456 }
457 int fd;
458 struct sockaddr_in addr;
459 socklen_t addrlen = sizeof(addr);
460 if(rc == 1)
461 {
462 fd = accept(listen_fd, (sockaddr*)&addr, &addrlen);
463 if(fd == -1)
464 {
465 res.error();
466 continue;
467 }
468 res.connection();
469 processes++;
470 int i;
471 for(i = 0; i < maxThreads && thread_status[i] == eUsed; i++)
472 ;
473 if(thread_status[i] == eFinished)
474 {
475 void *value_ptr;
476 pthread_join(thread_info[i], &value_ptr);
477 processes--;
478 }
479
480 thread_status[i] = eUsed;
481
482 pthread_attr_t attr;
483 if(pthread_attr_init(&attr)
484 || pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)
485 || pthread_attr_setstacksize(&attr, 32*1024))
486 fprintf(stderr, "Can't set thread attributes.\n");
487 thread_data *td = (thread_data *)malloc(sizeof(thread_data));
488 td->threadNum = i;
489 td->fd = fd;
490 memcpy(&td->addr, &addr, sizeof(addr));
491 td->debug = debug ? new Logit(*debug, i) : NULL;
492 #ifdef USE_SSL
493 td->ssl = use_ssl;
494 #endif
495 int p = pthread_create(&thread_info[i], &attr, smtp_worker, PVOID(td));
496 pthread_attr_destroy(&attr);
497 if(p)
498 {
499 fprintf(stderr, "Can't create a thread.\n");
500 return 1;
501 if(debug)
502 delete debug;
503 }
504 }
505 }
506 if(debug)
507 delete debug;
508 return 0;
509 }
510