]> git.proxmox.com Git - pmg-log-tracker.git/blob - pmg-log-tracker.c
bump version to 2.5.0
[pmg-log-tracker.git] / pmg-log-tracker.c
1 /*
2
3 (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
4
5 Proxmox Mail Tracker
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 Author: Dietmar Maurer <dietmar@proxmox.com>
21
22 See http://www.proxmox.com for more information
23
24 */
25
26 #define _GNU_SOURCE
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <glib.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <sys/time.h>
34 #include <time.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <stdint.h>
38 #include <zlib.h>
39 #include <fnmatch.h>
40
41 /*
42 We assume the syslog files belong to one host, i.e. we do not
43 consider the hostname at all
44 */
45
46 /*
47 REGEX: we do not use posix regular expressions (libc implementation
48 is too slow). fnmatch() is also slow.
49 Future versions may use our own implementation of a DFA. But currently we
50 just use strstr (no regex support at all)
51 */
52
53 //#define STRMATCH(pattern, string) (fnmatch (pattern, string, FNM_CASEFOLD) == 0)
54 #define STRMATCH(pattern, string) (strcasestr (string, pattern) != NULL)
55
56
57 //#define EPOOL_BLOCK_SIZE 512
58 //#define EPOOL_MAX_SIZE 128
59 #define EPOOL_BLOCK_SIZE 2048
60 #define EPOOL_MAX_SIZE 128
61 #define MAX_LOGFILES 32
62 //#define EPOOL_DEBUG
63 //#define DEBUG
64
65 typedef struct _SList SList;
66 struct _SList {
67 gpointer data;
68 SList *next;
69 };
70
71 typedef struct _NQList NQList;
72 struct _NQList {
73 char *from;
74 char *to;
75 char dstatus;
76 time_t ltime;
77 NQList *next;
78 };
79
80 typedef struct _TOList TOList;
81 struct _TOList {
82 char *to;
83 char *relay;
84 char dstatus;
85 time_t ltime;
86 TOList *next;
87 };
88
89 #define MatchTypeQID 1
90 #define MatchTypeRelLineNr 2
91
92 typedef struct _MatchList MatchList;
93 struct _MatchList {
94 unsigned int mtype;
95 char *id;
96 time_t ltime;
97 unsigned long rel_line_nr;
98 MatchList *next;
99 };
100
101 #ifdef DEBUG
102 GHashTable *smtpd_debug_alloc;
103 GHashTable *qmgr_debug_alloc;
104 GHashTable *filter_debug_alloc;
105 GHashTable *smtpd_debug_free;
106 GHashTable *qmgr_debug_free;
107 GHashTable *filter_debug_free;
108 #endif
109
110 // EPool: Entry related memory pools
111
112
113 #ifdef EPOOL_DEBUG
114 int ep_allocated;
115 int ep_maxalloc;
116 #endif
117
118 typedef struct _EPool EPool;
119 struct _EPool {
120 SList *blocks; // allocated usiing g_slice_alloc(EPOOL_BLOCK_SIZE)
121 SList *mblocks; // allocated use g_malloc
122 int cpos;
123 #ifdef EPOOL_DEBUG
124 int allocated;
125 #endif
126 };
127
128 typedef struct {
129 EPool ep;
130 GHashTable *smtpd_h;
131 GHashTable *qmgr_h;
132 GHashTable *filter_h;
133 //GHashTable *track_h;
134 gzFile stream[MAX_LOGFILES];
135 char *from;
136 char *to;
137 time_t year[MAX_LOGFILES];
138 time_t start;
139 time_t end;
140 time_t ctime;
141 MatchList *match_list;
142 char *server;
143 char *msgid;
144 char *strmatch;
145 unsigned long limit;
146 unsigned long count;
147 int verbose;
148 unsigned int exclude_greylist:1;
149 unsigned int exclude_ndrs:1;
150 } LParser;
151
152 typedef struct _SEntry SEntry;
153 typedef struct _QEntry QEntry;
154 typedef struct _FEntry FEntry;
155
156 typedef struct _LogEntry LogEntry;
157 typedef struct _LogList LogList;
158
159 struct _LogEntry {
160 const char *text;
161 unsigned long linenr;
162 LogEntry *next;
163 };
164
165 struct _LogList {
166 LogEntry *log;
167 LogEntry *logs_last; // pointer to last log (speedup add log)
168 };
169
170 // SEntry: Store SMTPD related logs
171
172 struct _SEntry {
173
174 EPool ep;
175 LogList loglist;
176
177 int pid;
178
179 SList *refs;
180
181 NQList *nqlist;
182
183 char *connect;
184
185 // time,rel_line_nr is used as cursor/ID
186 time_t ltime;
187 unsigned long rel_line_nr;
188
189 //unsigned int external:1; // not from local host
190 unsigned int disconnect:1;
191 unsigned int strmatch:1;
192
193 };
194
195 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
196
197 struct _QEntry {
198
199 EPool ep;
200 LogList loglist;
201
202 char *qid;
203
204 SEntry *smtpd;
205 FEntry *filter;
206
207 TOList *tolist;
208
209 unsigned int size;
210 char *from;
211 char *client;
212 char *msgid;
213
214 unsigned int cleanup:1;
215 unsigned int removed:1;
216 unsigned int filtered:1; // set when passed via lmtp to filter
217 unsigned int strmatch:1;
218 };
219
220 // FEntry: Store filter (proxprox) related logs
221
222 struct _FEntry {
223
224 EPool ep;
225 LogList loglist;
226
227 char *logid; // proxprox log id
228
229 TOList *tolist;
230
231 float ptime;
232
233 unsigned int finished:1;
234 unsigned int strmatch:1;
235 };
236
237 // Prototypes
238 void debug_error (char *msg, const char *line);
239
240 EPool *epool_init (EPool *ep);
241 void epool_free (EPool *ep);
242 gpointer epool_alloc (EPool *ep, int size);
243 char *epool_strndup (EPool *ep, const char *s, int len);
244 char *epool_strndup0 (EPool *ep, const char *s, int len);
245 char *epool_strdup (EPool *ep, const char *s);
246
247 void loglist_print (LogList *loglist);
248 void loglist_add (EPool *ep, LogList *loglist, const char *text, int len, unsigned long linenr);
249
250 SEntry *sentry_new (int pid, time_t ltime, unsigned long rel_line_nr);
251 SEntry *sentry_get (LParser *parser, int pid, time_t ltime, unsigned long rel_line_nr);
252 void sentry_ref_add (SEntry *sentry, QEntry *qentry);
253 int sentry_ref_del (SEntry *sentry, QEntry *qentry);
254 void sentry_ref_finalize (LParser *parser, SEntry *sentry);
255 int sentry_ref_rem_unneeded (LParser *parser, SEntry *sentry);
256 void sentry_nqlist_add (SEntry *sentry, time_t ltime, const char *from, int from_len,
257 const char *to, int to_len, char dstatus);
258 void sentry_print (LParser *parser, SEntry *sentry);
259 void sentry_set_connect (SEntry *sentry, const char *connect, int len);
260 void sentry_free_noremove (SEntry *sentry);
261 void sentry_free (LParser *parser, SEntry *sentry);
262 void sentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
263
264
265 QEntry *qentry_new (const char *qid);
266 QEntry *qentry_get (LParser *parser, const char *qid);
267 void qentry_tolist_add (QEntry *qentry, time_t ltime, char dstatus, const char *to,
268 int to_len, const char *relay, int relay_len);
269 void qentry_set_from (QEntry *qentry, const char *from, int len);
270 void qentry_set_msgid (QEntry *qentry, const char *msgid, int len);
271 void qentry_set_client (QEntry *qentry, const char *client, int len);
272 void qentry_print (LParser *parser, QEntry *qentry);
273 void qentry_finalize (LParser *parser, QEntry *qentry);
274 void qentry_free_noremove (QEntry *qentry);
275 void qentry_free (LParser *parser, QEntry *qentry);
276 void qentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
277
278
279 FEntry *fentry_new (const char *logid);
280 FEntry *fentry_get (LParser *parser, const char *logid);
281 void fentry_tolist_add (FEntry *fentry, char dstatus, const char *to,
282 int to_len, const char *qid, int qid_len);
283 void fentry_free_noremove (FEntry *fentry);
284 void fentry_free (LParser *parser, FEntry *fentry);
285 void fentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
286
287 LParser *parser_new ();
288 void parser_free (LParser *parser);
289
290 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
291
292 // Implementations
293
294 // Checksum Macros
295 #define PROXPROX 0xE0E4DEF0
296 #define PMG_SMTP_FILTER 0x0A85A6B7
297 #define POSTFIX_POSTSCREEN 0xD17E2019
298 #define POSTFIX_QMGR 0x48465316
299 #define POSTFIX_SMTP 0x4A466014
300 #define POSTFIX_LMTP 0x43466014
301 #define POSTFIX_LOCAL 0x484F05AF
302 #define POSTFIX_ERROR 0x4B5E13AE
303 #define POSTFIX_SMTPD 0x466014AE
304 #define POSTFIX_CLEANUP 0x05A8BAC1
305
306 //#define LOGPATH "./log/"
307 #define LOGPATH "/var/log/"
308 //#define LOGPATH "/root/testlog/"
309
310 static const char *logfiles[] = {
311 LOGPATH "syslog",
312 LOGPATH "syslog.1",
313 LOGPATH "syslog.2.gz",
314 LOGPATH "syslog.3.gz",
315 LOGPATH "syslog.4.gz",
316 LOGPATH "syslog.5.gz",
317 LOGPATH "syslog.6.gz",
318 LOGPATH "syslog.7.gz",
319 LOGPATH "syslog.8.gz",
320 LOGPATH "syslog.9.gz",
321 LOGPATH "syslog.10.gz",
322 LOGPATH "syslog.11.gz",
323 LOGPATH "syslog.12.gz",
324 LOGPATH "syslog.13.gz",
325 LOGPATH "syslog.14.gz",
326 LOGPATH "syslog.15.gz",
327 LOGPATH "syslog.16.gz",
328 LOGPATH "syslog.17.gz",
329 LOGPATH "syslog.18.gz",
330 LOGPATH "syslog.19.gz",
331 LOGPATH "syslog.20.gz",
332 LOGPATH "syslog.21.gz",
333 LOGPATH "syslog.22.gz",
334 LOGPATH "syslog.23.gz",
335 LOGPATH "syslog.24.gz",
336 LOGPATH "syslog.25.gz",
337 LOGPATH "syslog.26.gz",
338 LOGPATH "syslog.27.gz",
339 LOGPATH "syslog.28.gz",
340 LOGPATH "syslog.29.gz",
341 LOGPATH "syslog.30.gz",
342 LOGPATH "syslog.31.gz",
343 };
344
345 void
346 debug_error (char *msg, const char *line)
347 {
348 #ifdef DEBUG
349 fprintf (stderr, "ERROR: %s\n", msg);
350 if (line) fprintf (stderr, "LINE: %s\n", line);
351
352 G_BREAKPOINT();
353
354 exit (-1);
355 #endif
356 }
357
358 EPool *
359 epool_init (EPool *ep)
360 {
361 gpointer data;
362 SList *blocks;
363
364 data = g_slice_alloc0(EPOOL_BLOCK_SIZE);
365 blocks = (SList *)data;
366
367 #ifdef EPOOL_DEBUG
368 ep->allocated += EPOOL_BLOCK_SIZE;
369 ep_allocated += EPOOL_BLOCK_SIZE;
370 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
371 #endif
372
373
374 blocks->data = data;
375 blocks->next = NULL;
376
377 ep->blocks = blocks;
378 ep->cpos = sizeof (SList);
379
380 return ep;
381 }
382
383 void
384 epool_free (EPool *ep)
385 {
386 SList *l;
387 gpointer data;
388
389 #ifdef EPOOL_DEBUG
390 ep_allocated -= ep->allocated;
391 printf ("MEM: %d\n", ep_allocated);
392 #endif
393
394 #ifdef DEBUG
395 return;
396 #endif
397
398 l = ep->mblocks;
399 while (l) {
400 data = l->data;
401 l = l->next;
402 g_free (data);
403 }
404
405 l = ep->blocks;
406 while (l) {
407 data = l->data;
408 l = l->next;
409 g_slice_free1(EPOOL_BLOCK_SIZE, data);
410 }
411 }
412
413 gpointer
414 epool_alloc (EPool *ep, int size)
415 {
416 int rs = (size + 3) & ~3;
417 int space = EPOOL_BLOCK_SIZE - sizeof (SList) - ep->cpos;
418 gpointer data;
419
420 if (size > EPOOL_MAX_SIZE) {
421 SList *blocks;
422 if (space >= sizeof (SList)) {
423 blocks = (SList *)((char *)ep->blocks->data + ep->cpos);
424 ep->cpos += sizeof (SList);
425 } else {
426 blocks = (SList *)epool_alloc (ep, sizeof (SList));
427 }
428
429 data = g_malloc (size);
430 #ifdef EPOOL_DEBUG
431 ep->allocated += size;
432 ep_allocated += size;
433 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
434 #endif
435 blocks->data = data;
436 blocks->next = ep->mblocks;
437
438 ep->mblocks = blocks;
439
440 return data;
441
442 } else if (space >= rs) {
443 data = (char *)ep->blocks->data + ep->cpos;
444 ep->cpos += rs;
445
446 return data;
447
448 } else {
449 SList *blocks = (SList *)((char *)ep->blocks->data + ep->cpos);
450
451 data = g_slice_alloc0 (EPOOL_BLOCK_SIZE);
452 blocks->data = data;
453 blocks->next = ep->blocks;
454
455 ep->blocks = blocks;
456 ep->cpos = rs;
457
458 #ifdef EPOOL_DEBUG
459 ep->allocated += EPOOL_BLOCK_SIZE;
460 ep_allocated += EPOOL_BLOCK_SIZE;
461 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
462 #endif
463
464 return data;
465 }
466 }
467
468 char *
469 epool_strndup (EPool *ep, const char *s, int len)
470 {
471 int l = len + 1;
472 char *res = epool_alloc (ep, l);
473 strncpy (res, s, l);
474 return res;
475 }
476
477 char *
478 epool_strndup0 (EPool *ep, const char *s, int len)
479 {
480 char *res = epool_alloc (ep, len + 1);
481 strncpy (res, s, len);
482 res[len] = 0;
483
484 return res;
485 }
486
487 char *
488 epool_strdup (EPool *ep, const char *s)
489 {
490 int l = strlen (s) + 1;
491 char *res = epool_alloc (ep, l);
492 strncpy (res, s, l);
493 return res;
494 }
495
496 void
497 loglist_print (LogList *loglist)
498 {
499 LogEntry *log = loglist->log;
500 while (log) {
501 printf ("L%08lX %s", log->linenr, log->text);
502 log = log->next;
503 }
504 }
505
506
507 void
508 loglist_add (EPool *ep, LogList *loglist, const char *text, int len, unsigned long linenr)
509 {
510 LogEntry *log;
511
512 #ifdef DEBUG
513 if (len != strlen (text)) {
514 debug_error ("string with wrong len", NULL);
515 }
516 #endif
517 if (text[len] != 0) {
518 debug_error ("string is not null terminated", NULL);
519 return;
520 }
521
522 log = epool_alloc (ep, sizeof (LogEntry));
523
524 log->text = epool_strndup (ep, text, len);
525 log->linenr = linenr;
526 log->next = NULL;
527
528 if (loglist->logs_last) {
529 loglist->logs_last->next = log;
530 loglist->logs_last = log;
531 } else {
532 loglist->log = log;
533 loglist->logs_last = log;
534 }
535
536 return;
537 }
538
539 SEntry*
540 sentry_new (int pid, time_t ltime, unsigned long rel_line_nr)
541 {
542 SEntry *sentry;
543 SList *blocks;
544 int cpos;
545
546 sentry = (SEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
547 sentry->pid = pid;
548 sentry->ltime = ltime;
549 sentry->rel_line_nr = rel_line_nr;
550
551 #ifdef EPOOL_DEBUG
552 sentry->ep.allocated += EPOOL_BLOCK_SIZE;
553 ep_allocated += EPOOL_BLOCK_SIZE;
554 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
555 #endif
556
557 #ifdef DEBUG
558 {
559 SEntry *se;
560 if ((se = g_hash_table_lookup (smtpd_debug_alloc, sentry))) {
561 debug_error ("SEntry already alloced", NULL);
562 } else {
563 g_hash_table_insert (smtpd_debug_alloc, sentry, sentry);
564 }
565 }
566 #endif
567
568 cpos = sizeof (SEntry);
569
570 blocks = (SList *)((char *)sentry + cpos);
571
572 cpos += sizeof (SList);
573
574 blocks->data = sentry;
575 blocks->next = NULL;
576
577 sentry->ep.blocks = blocks;
578 sentry->ep.cpos = cpos;
579
580 return sentry;
581 }
582
583 SEntry *
584 sentry_get (LParser *parser, int pid, time_t ltime, unsigned long rel_line_nr)
585 {
586 SEntry *sentry;
587
588 if ((sentry = g_hash_table_lookup (parser->smtpd_h, &pid))) {
589 return sentry;
590 } else {
591
592 if ((sentry = sentry_new (pid, ltime, rel_line_nr))) {
593 g_hash_table_insert (parser->smtpd_h, &sentry->pid, sentry);
594 }
595
596 return sentry;
597 }
598 }
599
600 void
601 sentry_ref_add (SEntry *sentry, QEntry *qentry)
602 {
603 SList *l;
604
605 if (qentry->smtpd) {
606 if (qentry->smtpd != sentry) {
607 debug_error ("qentry ref already set", NULL);
608 }
609 return;
610 }
611
612 l = sentry->refs;
613 while (l) {
614 if (l->data == qentry) return;
615 l = l->next;
616 }
617
618 l = epool_alloc (&sentry->ep, sizeof (SList));
619
620 qentry->smtpd = sentry;
621
622 l->data = qentry;
623 l->next = sentry->refs;
624
625 sentry->refs = l;
626 }
627
628 int
629 sentry_ref_del (SEntry *sentry, QEntry *qentry)
630 {
631 SList *l = sentry->refs;
632 int count = 0;
633
634 if (!qentry->smtpd) {
635 debug_error ("qentry does not hav a qentry ref", NULL);
636 return 0;
637 }
638
639 l = sentry->refs;
640
641 while (l) {
642 QEntry *qe = (QEntry *)l->data;
643 if (qe == qentry) {
644 l->data = NULL;
645 } else {
646 if (qe && qe->cleanup) count++;
647 }
648 l = l->next;
649 }
650
651 return count;
652 }
653
654 void
655 sentry_ref_finalize (LParser *parser, SEntry *sentry)
656 {
657 SList *l = sentry->refs;
658
659 int count = 0;
660
661 while (l) {
662 SList *cl = l;
663 QEntry *qe = l->data;
664
665 l = l->next;
666
667 if (!qe) continue;
668
669 count++;
670
671 if (!qe->removed) continue;
672
673 FEntry *fe = qe->filter;
674
675 if (fe && !fe->finished) continue;
676
677 count--;
678
679 qentry_print (parser, qe);
680
681 cl->data = NULL;
682 qe->smtpd = NULL;
683
684 qentry_free (parser, qe);
685
686 if (fe) fentry_free (parser, fe);
687
688 }
689
690 if (!count) sentry_free_noremove (sentry);
691 }
692
693 int
694 sentry_ref_rem_unneeded (LParser *parser, SEntry *sentry)
695 {
696 SList *l = sentry->refs;
697 int count = 0;
698
699 while (l) {
700 QEntry *qe = (QEntry *)l->data;
701
702 if (qe) {
703 if (!qe->cleanup) {
704 qe->smtpd = NULL;
705 qentry_free (parser, qe);
706 l->data = NULL;
707 } else {
708 count++;
709 }
710 }
711
712 l = l->next;
713 }
714
715 return count;
716 }
717
718 void
719 sentry_nqlist_add (SEntry *sentry, time_t ltime, const char *from, int from_len,
720 const char *to, int to_len, char dstatus)
721 {
722 NQList *nq = (NQList *)epool_alloc (&sentry->ep, sizeof (NQList));
723
724 nq->from = epool_strndup0 (&sentry->ep, from, from_len);
725 nq->to = epool_strndup0 (&sentry->ep, to, to_len);
726 nq->dstatus = dstatus;
727
728 nq->next = sentry->nqlist;
729 nq->ltime = ltime;
730 sentry->nqlist = nq;
731 }
732
733 void
734 sentry_print (LParser *parser, SEntry *sentry)
735 {
736 NQList *nq;
737
738 if (parser->msgid) return;
739
740 if (parser->server) {
741 if (!sentry->connect) return;
742 if (!strcasestr (sentry->connect, parser->server)) return;
743 }
744
745 MatchList *match = parser->match_list;
746 if (match) {
747 int found = 0;
748 while(match) {
749 if (match->mtype == MatchTypeQID) {
750 return;
751 } else if (match->mtype == MatchTypeRelLineNr) {
752 if (match->ltime == sentry->ltime && match->rel_line_nr == sentry->rel_line_nr) {
753 found = 1;
754 break;
755 }
756 } else {
757 g_error("implement me");
758 }
759 match = match->next;
760 }
761 if (!found) return;
762 }
763
764 if (parser->from || parser->to ||
765 parser->exclude_greylist || parser->exclude_ndrs) {
766 nq = sentry->nqlist;
767 int found = 0;
768 while (nq) {
769 if (parser->from) {
770 if (!*(parser->from)) {
771 if (*(nq->from)) nq->dstatus = 0;
772 } else if (!STRMATCH(parser->from, nq->from)) {
773 nq->dstatus = 0;
774 }
775 }
776
777 if (parser->exclude_greylist && nq->dstatus == 'G') nq->dstatus = 0;
778
779 if (parser->exclude_ndrs && nq->from && !*nq->from) nq->dstatus = 0;
780
781 if (parser->to && nq->to && !STRMATCH(parser->to, nq->to)) {
782 nq->dstatus = 0;
783 }
784
785 if (nq->dstatus != 0) found = 1;
786
787 nq = nq->next;
788 }
789 if (!found) return;
790 }
791
792 if (parser->strmatch && !sentry->strmatch) return;
793
794 if (parser->verbose) {
795
796 printf ("SMTPD: T%08lXL%08lX\n", sentry->ltime, sentry->rel_line_nr);
797
798 printf ("CTIME: %08lX\n", parser->ctime);
799
800 if (sentry->connect) { printf ("CLIENT: %s\n", sentry->connect); }
801 //printf ("EXTERNAL: %d\n", sentry->external);
802
803 }
804
805 nq = sentry->nqlist;
806 while (nq) {
807 if (nq->from && nq->to && nq->dstatus) {
808 printf ("TO:%08lX:T%08lXL%08lX:%c: from <%s> to <%s>\n", nq->ltime,
809 sentry->ltime, sentry->rel_line_nr, nq->dstatus,
810 nq->from, nq->to);
811 parser->count++;
812 }
813 nq = nq->next;
814 }
815
816 if (!parser->verbose) { fflush (stdout); return; }
817
818 if (parser->verbose > 1) {
819 printf ("LOGS:\n");
820 loglist_print (&sentry->loglist);
821 }
822
823 printf ("\n");
824
825 fflush (stdout);
826 }
827
828 void
829 sentry_set_connect (SEntry *sentry, const char *connect, int len)
830 {
831 if (sentry->connect) {
832 #ifdef DEBUG
833 if (strncmp (sentry->connect, connect, len)) {
834 debug_error ("duplicate connect", NULL);
835 }
836 #endif
837 } else {
838 sentry->connect = epool_strndup0 (&sentry->ep, connect, len);
839 }
840 }
841
842 void
843 sentry_free_noremove (SEntry *sentry)
844 {
845 SList *l;
846 gpointer data;
847
848 #ifdef EPOOL_DEBUG
849 ep_allocated -= sentry->ep.allocated;
850 printf ("MEM: %d\n", ep_allocated);
851 #endif
852
853 #ifdef DEBUG
854 {
855 SEntry *se;
856 if ((se = g_hash_table_lookup (smtpd_debug_free, sentry))) {
857 debug_error ("SEntry already freed", NULL);
858 } else {
859 g_hash_table_insert (smtpd_debug_free, sentry, sentry);
860 }
861 }
862 return;
863 #endif
864
865 l = sentry->ep.mblocks;
866 while (l) {
867 data = l->data;
868 l = l->next;
869 g_free (data);
870 }
871
872 l = sentry->ep.blocks;
873 while (l) {
874 data = l->data;
875 l = l->next;
876 g_slice_free1(EPOOL_BLOCK_SIZE, data);
877 }
878 }
879
880 void
881 sentry_free (LParser *parser, SEntry *sentry)
882 {
883 g_hash_table_remove (parser->smtpd_h, &sentry->pid);
884
885 sentry_free_noremove (sentry);
886 }
887
888 void
889 sentry_cleanup_hash (gpointer key,
890 gpointer value,
891 gpointer user_data)
892 {
893 SEntry *se = value;
894 LParser *parser = (LParser *)user_data;
895
896 sentry_print (parser, se);
897 sentry_free_noremove (se);
898 }
899
900 #ifdef DEBUG
901 void
902 sentry_debug_alloc (gpointer key,
903 gpointer value,
904 gpointer user_data)
905 {
906 LParser *parser = (LParser *)user_data;
907 SEntry *se = value;
908 SEntry *fe;
909
910 if ((fe = g_hash_table_lookup (smtpd_debug_free, se))) {
911 return;
912 }
913
914 printf ("FOUND ALLOCATED SENTRY:\n");
915 sentry_print (parser, se);
916
917 exit (-1);
918 }
919 #endif
920
921 // QEntry
922
923 QEntry*
924 qentry_new (const char *qid)
925 {
926 QEntry *qentry;
927 SList *blocks;
928 int cpos;
929 char *qid_cp;
930
931 qentry = (QEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
932
933 #ifdef EPOOL_DEBUG
934 qentry->ep.allocated += EPOOL_BLOCK_SIZE;
935 ep_allocated += EPOOL_BLOCK_SIZE;
936 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
937 #endif
938
939 #ifdef DEBUG
940 {
941 QEntry *qe;
942 if ((qe = g_hash_table_lookup (qmgr_debug_alloc, qentry))) {
943 debug_error ("QEntry already alloced", NULL);
944 } else {
945 g_hash_table_insert (qmgr_debug_alloc, qentry, qentry);
946 }
947 }
948 #endif
949
950 cpos = sizeof (QEntry);
951
952 blocks = (SList *)((char *)qentry + cpos);
953
954 cpos += sizeof (SList);
955
956 blocks->data = qentry;
957 blocks->next = NULL;
958
959 qentry->qid = qid_cp = (char *)qentry + cpos;
960 while ((*qid_cp++ = *qid++)) cpos++;
961
962 cpos = (cpos + 4) & ~3;
963
964 qentry->ep.blocks = blocks;
965 qentry->ep.cpos = cpos;;
966
967 return qentry;
968 }
969
970 void
971 qentry_tolist_add (QEntry *qentry, time_t ltime, char dstatus, const char *to, int to_len,
972 const char *relay, int relay_len)
973 {
974 TOList *tl = (TOList *)epool_alloc (&qentry->ep, sizeof (TOList));
975
976 tl->to = epool_strndup0 (&qentry->ep, to, to_len);
977 tl->relay = epool_strndup0 (&qentry->ep, relay, relay_len);
978 tl->dstatus = dstatus;
979 tl->ltime = ltime;
980 tl->next = qentry->tolist;
981
982 qentry->tolist = tl;
983 }
984
985 void
986 qentry_set_from (QEntry *qentry, const char *from, int len)
987 {
988 if (qentry->from) {
989 #ifdef DEBUG
990 if (strncmp (qentry->from, from, len)) {
991 debug_error ("duplicate from", NULL);
992 }
993 #endif
994 } else {
995 qentry->from = epool_strndup0 (&qentry->ep, from, len);
996 }
997 }
998
999 void
1000 qentry_set_msgid (QEntry *qentry, const char *msgid, int len)
1001 {
1002 if (qentry->msgid) {
1003 #ifdef DEBUG
1004 if (strncmp (qentry->msgid, msgid, len)) {
1005 debug_error ("duplicate msgid", NULL);
1006 }
1007 #endif
1008 } else {
1009 qentry->msgid = epool_strndup0 (&qentry->ep, msgid, len);
1010 }
1011 }
1012
1013 void
1014 qentry_set_client (QEntry *qentry, const char *client, int len)
1015 {
1016 if (qentry->client) {
1017 #ifdef DEBUG
1018 if (strncmp (qentry->client, client, len)) {
1019 debug_error ("duplicate client", NULL);
1020 }
1021 #endif
1022 } else {
1023 qentry->client = epool_strndup0 (&qentry->ep, client, len);
1024 }
1025 }
1026
1027 void
1028 qentry_print (LParser *parser, QEntry *qentry)
1029 {
1030 TOList *tl, *fl;
1031 SEntry *se = qentry->smtpd;
1032 FEntry *fe = qentry->filter;
1033
1034 if (parser->msgid) {
1035 if (!qentry->msgid) return;
1036 if (strcasecmp (parser->msgid, qentry->msgid)) return;
1037 }
1038
1039 MatchList *match = parser->match_list;
1040 if (match) {
1041 int found = 0;
1042 while(match) {
1043 if (match->mtype == MatchTypeQID) {
1044 if ((fe && !strcmp (fe->logid, match->id)) ||
1045 (!strcmp (qentry->qid, match->id))) {
1046 found = 1;
1047 break;
1048 }
1049 } else if (match->mtype == MatchTypeRelLineNr) {
1050 if (se && match->ltime == se->ltime && match->rel_line_nr == se->rel_line_nr) {
1051 found = 1;
1052 break;
1053 }
1054 } else {
1055 g_error("implement me");
1056 }
1057 match = match->next;
1058 }
1059 if (!found) return;
1060 }
1061
1062 if (parser->server) {
1063 int found = 0;
1064 if (se && se->connect && strcasestr (se->connect, parser->server)) found = 1;
1065 if (qentry->client && strcasestr (qentry->client, parser->server)) found = 1;
1066
1067 if (!found) return;
1068 }
1069
1070 if (parser->from) {
1071 if (!qentry->from) return;
1072 if (!*(parser->from)) {
1073 if (*(qentry->from)) return;
1074 } else if (!STRMATCH(parser->from, qentry->from)) {
1075 return;
1076 }
1077 } else {
1078 if (parser->exclude_ndrs && qentry->from && !*qentry->from) return;
1079 }
1080
1081 if (parser->to) {
1082 tl = qentry->tolist;
1083 int found = 0;
1084 while (tl) {
1085 if (parser->to && !STRMATCH(parser->to, tl->to)) {
1086 tl->to = NULL;
1087 } else {
1088 found = 1;
1089 }
1090 tl = tl->next;
1091 }
1092 if (!found) return;
1093 }
1094
1095 if (parser->strmatch &&
1096 !(qentry->strmatch || (se && se->strmatch) || (fe && fe->strmatch)))
1097 return;
1098
1099
1100 if (parser->verbose) {
1101
1102 printf ("QENTRY: %s\n", qentry->qid);
1103
1104 printf ("CTIME: %08lX\n", parser->ctime);
1105 printf ("SIZE: %u\n", qentry->size);
1106
1107 if (qentry->client) {
1108 printf ("CLIENT: %s\n", qentry->client);
1109 } else if (se && se->connect) {
1110 printf ("CLIENT: %s\n", se->connect);
1111 }
1112
1113 if (qentry->msgid) { printf ("MSGID: %s\n", qentry->msgid); }
1114
1115 }
1116
1117 tl = qentry->tolist;
1118 while (tl) {
1119 if (tl->to) {
1120 fl = NULL;
1121 if (fe && tl->dstatus == '2') {
1122 fl = fe->tolist;
1123 while (fl) {
1124 if (fl->to && !strcmp (tl->to, fl->to)) {
1125 break;
1126 }
1127 fl = fl->next;
1128 }
1129 }
1130 char *to;
1131 char dstatus;
1132 char *relay;
1133
1134 if (fl) {
1135 to = fl->to;
1136 dstatus = fl->dstatus;
1137 relay = fl->relay;
1138 } else {
1139 to = tl->to;
1140 dstatus = tl->dstatus;
1141 relay = tl->relay;
1142 }
1143
1144 printf ("TO:%08lX:%s:%c: from <%s> to <%s> (%s)\n", tl->ltime, qentry->qid, dstatus, qentry->from ? qentry->from : "", to ? to : "", relay ? relay : "none");
1145
1146 parser->count++;
1147 }
1148 tl = tl->next;
1149 }
1150
1151
1152 if (!parser->verbose) { fflush (stdout); return; }
1153
1154 if (parser->verbose > 1) {
1155
1156 if (se && se->loglist.log) {
1157 printf ("SMTP:\n");
1158 loglist_print (&se->loglist);
1159 }
1160
1161 if (fe && fe->loglist.log) {
1162 printf ("FILTER: %s\n", fe->logid);
1163 loglist_print (&fe->loglist);
1164 }
1165
1166 if (qentry->loglist.log) {
1167 printf ("QMGR:\n");
1168 loglist_print (&qentry->loglist);
1169 }
1170 }
1171
1172 printf ("\n");
1173
1174 fflush (stdout);
1175
1176 //sleep (1);
1177 }
1178
1179 QEntry *
1180 qentry_get (LParser *parser, const char *qid)
1181 {
1182 QEntry *qentry;
1183
1184 if ((qentry = g_hash_table_lookup (parser->qmgr_h, qid))) {
1185 return qentry;
1186 } else {
1187 if ((qentry = qentry_new (qid))) {
1188 g_hash_table_insert (parser->qmgr_h, qentry->qid, qentry);
1189 }
1190
1191 return qentry;
1192 }
1193 }
1194
1195 void
1196 qentry_free_noremove (QEntry *qentry)
1197 {
1198 SList *l;
1199 gpointer data;
1200 SEntry *se;
1201
1202 if ((se = qentry->smtpd)) {
1203 if (sentry_ref_del (se, qentry) == 0) {
1204 if (se->disconnect) {
1205 sentry_free_noremove (se);
1206 }
1207 }
1208 }
1209
1210 #ifdef EPOOL_DEBUG
1211 ep_allocated -= qentry->ep.allocated;
1212 printf ("MEM: %d\n", ep_allocated);
1213 #endif
1214
1215 #ifdef DEBUG
1216 {
1217 QEntry *qe;
1218 if ((qe = g_hash_table_lookup (qmgr_debug_free, qentry))) {
1219 debug_error ("QEntry already freed", NULL);
1220 } else {
1221 g_hash_table_insert (qmgr_debug_free, qentry, qentry);
1222 }
1223 }
1224 return;
1225 #endif
1226
1227 l = qentry->ep.mblocks;
1228 while (l) {
1229 data = l->data;
1230 l = l->next;
1231 g_free (data);
1232 }
1233
1234 l = qentry->ep.blocks;
1235 while (l) {
1236 data = l->data;
1237 l = l->next;
1238 g_slice_free1(EPOOL_BLOCK_SIZE, data);
1239 }
1240 }
1241
1242 void
1243 qentry_free (LParser *parser, QEntry *qentry)
1244 {
1245 g_hash_table_remove (parser->qmgr_h, qentry->qid);
1246
1247 qentry_free_noremove (qentry);
1248 }
1249
1250 void
1251 qentry_cleanup_hash (gpointer key,
1252 gpointer value,
1253 gpointer user_data)
1254 {
1255 QEntry *qe = value;
1256 LParser *parser = (LParser *)user_data;
1257
1258 qentry_print (parser, qe);
1259 qentry_free_noremove (qe);
1260 }
1261
1262 void
1263 qentry_finalize (LParser *parser, QEntry *qentry)
1264 {
1265 if (qentry && qentry->removed) {
1266 SEntry *se = qentry->smtpd;
1267
1268 if (se && !se->disconnect) return;
1269
1270 FEntry *fe = qentry->filter;
1271
1272 if (fe && !fe->finished) return;
1273
1274 qentry_print (parser, qentry);
1275 qentry_free (parser, qentry);
1276
1277 if (fe) fentry_free (parser, fe);
1278 }
1279 }
1280
1281 // FEntry
1282
1283 FEntry*
1284 fentry_new (const char *logid)
1285 {
1286 FEntry *fentry;
1287 SList *blocks;
1288 int cpos;
1289 char *logid_cp;
1290
1291 fentry = (FEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
1292
1293 #ifdef EPOOL_DEBUG
1294 fentry->ep.allocated += EPOOL_BLOCK_SIZE;
1295 ep_allocated += EPOOL_BLOCK_SIZE;
1296 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
1297 #endif
1298
1299 #ifdef DEBUG
1300 {
1301 FEntry *fe;
1302 if ((fe = g_hash_table_lookup (filter_debug_alloc, fentry))) {
1303 debug_error ("FEntry already alloced", NULL);
1304 } else {
1305 g_hash_table_insert (filter_debug_alloc, fentry, fentry);
1306 }
1307 }
1308 #endif
1309
1310 cpos = sizeof (FEntry);
1311
1312 blocks = (SList *)((char *)fentry + cpos);
1313
1314 cpos += sizeof (SList);
1315
1316 blocks->data = fentry;
1317 blocks->next = NULL;
1318
1319 fentry->logid = logid_cp = (char *)fentry + cpos;
1320 while ((*logid_cp++ = *logid++)) cpos++;
1321 cpos = (cpos + 4) & ~3;
1322
1323 fentry->ep.blocks = blocks;
1324 fentry->ep.cpos = cpos;;
1325
1326 return fentry;
1327 }
1328
1329 FEntry *
1330 fentry_get (LParser *parser, const char *logid)
1331 {
1332 FEntry *fentry;
1333
1334 if ((fentry = g_hash_table_lookup (parser->filter_h, logid))) {
1335 return fentry;
1336 } else {
1337 if ((fentry = fentry_new (logid))) {
1338 g_hash_table_insert (parser->filter_h, fentry->logid, fentry);
1339 }
1340
1341 return fentry;
1342 }
1343 }
1344
1345 void
1346 fentry_tolist_add (FEntry *fentry, char dstatus, const char *to, int to_len,
1347 const char *qid, int qid_len)
1348 {
1349 TOList *tl = (TOList *)epool_alloc (&fentry->ep, sizeof (TOList));
1350
1351 tl->to = epool_strndup0 (&fentry->ep, to, to_len);
1352
1353 if (qid) {
1354 tl->relay = epool_strndup0 (&fentry->ep, qid, qid_len);
1355 } else {
1356 tl->relay = NULL;
1357 }
1358 tl->dstatus = dstatus;
1359 tl->next = fentry->tolist;
1360
1361 fentry->tolist = tl;
1362 }
1363
1364 void
1365 fentry_free_noremove (FEntry *fentry)
1366 {
1367 SList *l;
1368 gpointer data;
1369
1370 #ifdef EPOOL_DEBUG
1371 ep_allocated -= fentry->ep.allocated;
1372 printf ("MEM: %d\n", ep_allocated);
1373 #endif
1374
1375 #ifdef DEBUG
1376 {
1377 FEntry *fe;
1378 if ((fe = g_hash_table_lookup (filter_debug_free, fentry))) {
1379 debug_error ("FEntry already freed", NULL);
1380 } else {
1381 g_hash_table_insert (filter_debug_free, fentry, fentry);
1382 }
1383 }
1384 return;
1385 #endif
1386
1387 l = fentry->ep.mblocks;
1388 while (l) {
1389 data = l->data;
1390 l = l->next;
1391 g_free (data);
1392 }
1393
1394 l = fentry->ep.blocks;
1395 while (l) {
1396 data = l->data;
1397 l = l->next;
1398 g_slice_free1(EPOOL_BLOCK_SIZE, data);
1399 }
1400 }
1401
1402 void
1403 fentry_free (LParser *parser, FEntry *fentry)
1404 {
1405 g_hash_table_remove (parser->filter_h, fentry->logid);
1406
1407 fentry_free_noremove (fentry);
1408 }
1409
1410 void
1411 fentry_cleanup_hash (gpointer key,
1412 gpointer value,
1413 gpointer user_data)
1414 {
1415 FEntry *fe = value;
1416
1417 fentry_free_noremove (fe);
1418 }
1419
1420 // Parser
1421
1422 LParser*
1423 parser_new ()
1424 {
1425 LParser *parser = g_malloc0 (sizeof (LParser));
1426 struct timeval tv;
1427 struct tm *ltime;
1428 int i;
1429
1430 epool_init (&parser->ep);
1431
1432 if (!(parser->smtpd_h = g_hash_table_new (g_int_hash, g_int_equal))) {
1433 return NULL;
1434 }
1435
1436 if (!(parser->qmgr_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1437 return NULL;
1438 }
1439
1440 if (!(parser->filter_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1441 return NULL;
1442 }
1443
1444 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1445 //return NULL;
1446 //}
1447
1448 for (i = 0; i < MAX_LOGFILES; i++) {
1449 gettimeofday(&tv, NULL);
1450 tv.tv_sec -= 3600*24*i;
1451 ltime = localtime (&tv.tv_sec);
1452 parser->year[i] = ltime->tm_year + 1900;
1453 }
1454
1455 return parser;
1456 }
1457
1458 void
1459 parser_free (LParser *parser)
1460 {
1461 int i;
1462
1463 for (i = 0; i < MAX_LOGFILES; i++) {
1464 if (parser->stream[i]) gzclose (parser->stream[i]);
1465 }
1466
1467 epool_free (&parser->ep);
1468
1469 g_hash_table_destroy (parser->smtpd_h);
1470 g_hash_table_destroy (parser->qmgr_h);
1471 g_hash_table_destroy (parser->filter_h);
1472 //g_hash_table_destroy (parser->track_h);
1473
1474 g_free (parser);
1475 }
1476
1477 #if 0
1478 char *
1479 parser_track (LParser *parser, const char *qid, gboolean insert)
1480 {
1481 char *res;
1482
1483 if ((res = g_hash_table_lookup (parser->track_h, qid))) {
1484 return res;
1485 } else {
1486 if (insert && (res = epool_strdup (&parser->ep, qid))) {
1487 g_hash_table_insert (parser->track_h, res, res);
1488 return res;
1489 }
1490 }
1491 return NULL;
1492 }
1493 #endif
1494
1495 #define LINEBUFSIZE 8192
1496 static int cur_year;
1497 static int cur_month = 0;
1498 static int cal_mtod[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1499
1500 static time_t
1501 mkgmtime (struct tm *tm)
1502 {
1503 time_t res;
1504
1505 int year = tm->tm_year + 1900;
1506 int mon = tm->tm_mon;
1507
1508 res = (year - 1970) * 365 + cal_mtod[mon];
1509
1510 // leap year corrections (gregorian calendar)
1511 if (mon <= 1) year -= 1;
1512 res += (year - 1968) / 4;
1513 res -= (year - 1900) / 100;
1514 res += (year - 1600) / 400;
1515
1516 res += tm->tm_mday - 1;
1517 res = res*24 + tm->tm_hour;
1518 res = res*60 + tm->tm_min;
1519 res = res*60 + tm->tm_sec;
1520
1521 return res;
1522 }
1523
1524 #define JAN (('J'<<16)|('a'<<8)|'n')
1525 #define FEB (('F'<<16)|('e'<<8)|'b')
1526 #define MAR (('M'<<16)|('a'<<8)|'r')
1527 #define APR (('A'<<16)|('p'<<8)|'r')
1528 #define MAY (('M'<<16)|('a'<<8)|'y')
1529 #define JUN (('J'<<16)|('u'<<8)|'n')
1530 #define JUL (('J'<<16)|('u'<<8)|'l')
1531 #define AUG (('A'<<16)|('u'<<8)|'g')
1532 #define SEP (('S'<<16)|('e'<<8)|'p')
1533 #define OCT (('O'<<16)|('c'<<8)|'t')
1534 #define NOV (('N'<<16)|('o'<<8)|'v')
1535 #define DEC (('D'<<16)|('e'<<8)|'c')
1536
1537 time_t
1538 parse_time (const char **text, int len)
1539 {
1540 time_t ltime = 0;
1541
1542 int year = cur_year;
1543
1544 int mon = 0;
1545 int mday = 0;
1546 int hour = 0;
1547 int min = 0;
1548 int sec = 0;
1549 int found;
1550
1551 const char *line = *text;
1552
1553 if (len == (LINEBUFSIZE - 1)) {
1554 debug_error ("skipping long line data", line);
1555 return 0;
1556 }
1557
1558 if (len < 15) {
1559 debug_error ("skipping short line data", line);
1560 return 0;
1561 }
1562
1563 // parse month
1564 int csum = (line[0]<<16) + (line[1]<<8) + line[2];
1565
1566 switch (csum) {
1567 case JAN: mon = 0; break;
1568 case FEB: mon = 1; break;
1569 case MAR: mon = 2; break;
1570 case APR: mon = 3; break;
1571 case MAY: mon = 4; break;
1572 case JUN: mon = 5; break;
1573 case JUL: mon = 6; break;
1574 case AUG: mon = 7; break;
1575 case SEP: mon = 8; break;
1576 case OCT: mon = 9; break;
1577 case NOV: mon = 10; break;
1578 case DEC: mon = 11; break;
1579 default:
1580 debug_error ("unable to parse month", line);
1581 return 0;
1582 }
1583
1584 // year change heuristik
1585 if (cur_month == 11 && mon == 0) {
1586 year++;
1587 }
1588 if (mon > cur_month) cur_month = mon;
1589
1590 ltime = (year - 1970)*365 + cal_mtod[mon];
1591
1592 // leap year corrections (gregorian calendar)
1593 if (mon <= 1) year -= 1;
1594 ltime += (year - 1968) / 4;
1595 ltime -= (year - 1900) / 100;
1596 ltime += (year - 1600) / 400;
1597
1598 const char *cpos = line + 3;
1599
1600 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1601 if (!found) {
1602 debug_error ("missing spaces after month", line);
1603 return 0;
1604 }
1605
1606 found = 0; while (isdigit (*cpos)) { mday = mday*10 + *cpos - '0'; cpos++; found++; }
1607 if (found < 1 || found > 2) {
1608 debug_error ("unable to parse day of month", line);
1609 return 0;
1610 }
1611
1612 ltime += mday - 1;
1613
1614 found = 0; while (isspace (*cpos)) { cpos++; found++; }
1615 if (!found) {
1616 debug_error ("missing spaces after day of month", line);
1617 return 0;
1618 }
1619
1620 found = 0; while (isdigit (*cpos)) { hour = hour*10 + *cpos - '0'; cpos++; found++; }
1621 if (found < 1 || found > 2) {
1622 debug_error ("unable to parse hour", line);
1623 return 0;
1624 }
1625
1626 ltime *= 24;
1627 ltime += hour;
1628
1629 if (*cpos != ':') {
1630 debug_error ("missing collon after hour", line);
1631 return 0;
1632 }
1633 cpos++;
1634
1635 found = 0; while (isdigit (*cpos)) { min = min*10 + *cpos - '0'; cpos++; found++; }
1636 if (found < 1 || found > 2) {
1637 debug_error ("unable to parse minute", line);
1638 return 0;
1639 }
1640
1641 ltime *= 60;
1642 ltime += min;
1643
1644 if (*cpos != ':') {
1645 debug_error ("missing collon after minute", line);
1646 return 0;
1647 }
1648 cpos++;
1649
1650 found = 0; while (isdigit (*cpos)) { sec = sec*10 + *cpos - '0'; cpos++; found++; }
1651 if (found < 1 || found > 2) {
1652 debug_error ("unable to parse second", line);
1653 return 0;
1654 }
1655
1656 ltime *= 60;
1657 ltime += sec;
1658
1659 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1660 if (!found) {
1661 debug_error ("missing spaces after time", line);
1662 return 0;
1663 }
1664
1665 *text = cpos;
1666
1667 return ltime;
1668 }
1669
1670
1671 int
1672 parser_count_files (LParser *parser)
1673 {
1674 int i;
1675 time_t start = parser->start;
1676 char linebuf[LINEBUFSIZE];
1677 const char *line;
1678 gzFile stream;
1679
1680 for (i = 0; i < (MAX_LOGFILES - 1); i++) {
1681 cur_year = parser->year[i];
1682 cur_month = 0;
1683
1684 if ((stream = gzopen (logfiles[i], "r"))) {
1685 if ((line = gzgets (stream, linebuf, LINEBUFSIZE))) {
1686 if (parse_time (&line, strlen (line)) < start) {
1687 break;
1688 }
1689 } else {
1690 return i;
1691 }
1692 gzclose (stream);
1693 } else {
1694 return i;
1695 }
1696 }
1697
1698 return i + 1;
1699 }
1700
1701 static char *
1702 parse_qid (const char **text, char *out, char delim, int maxlen)
1703 {
1704 const char *idx;
1705 char *copy = out;
1706
1707 int found = 0;
1708 idx = *text;
1709 while (isxdigit (*idx)) { *copy++ = *idx++; found++; if (found > maxlen) break; }
1710
1711 if (found > 1 && found < maxlen &&
1712 ((delim && (*idx == delim)) || (!delim && isspace (*idx)))) {
1713 *copy = 0;
1714 idx++;
1715 while (isspace (*idx)) idx++;
1716 *text = idx;
1717 return out;
1718 }
1719 return NULL;
1720 }
1721
1722 void
1723 print_usage (const char *name)
1724 {
1725 fprintf (stderr, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name);
1726 fprintf (stderr, "\t-f SENDER mails from SENDER\n");
1727 fprintf (stderr, "\t-t RECIPIENT mails to RECIPIENT\n");
1728 fprintf (stderr, "\t-h Server Server IP or Hostname\n");
1729 fprintf (stderr, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1730 fprintf (stderr, "\t or seconds since epoch\n");
1731 fprintf (stderr, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1732 fprintf (stderr, "\t or seconds since epoch\n");
1733 fprintf (stderr, "\t-m MSGID message ID (exact match)\n");
1734 fprintf (stderr, "\t-q QID queue ID (exact match), can be\n");
1735 fprintf (stderr, "\t specified multiple times.\n");
1736 fprintf (stderr, "\t-x STRING search for strings\n");
1737 fprintf (stderr, "\t-l LIMIT print max limit entries\n");
1738 fprintf (stderr, "\t-g exclude greylist entries\n");
1739 fprintf (stderr, "\t-n exclude NDR entries\n");
1740 fprintf (stderr, "\t-v verbose output (no logs)\n");
1741 fprintf (stderr, "\t-vv verbose output with logs\n");
1742 }
1743
1744
1745 // gzgets is ways too slow, so we do it our own way
1746
1747 static char
1748 mygzgetc (gzFile stream)
1749 {
1750 int br;
1751 static char readbuf[16384];
1752 static char *readend = readbuf + sizeof (readbuf);
1753 static char *readpos = readbuf + sizeof (readbuf);
1754
1755 if (readpos < readend) return *readpos++;
1756
1757 if ((br = gzread (stream, readbuf, sizeof (readbuf))) <= 0) {
1758 return -1;
1759 } else {
1760 readpos = readbuf;
1761 readend = readbuf + br;
1762
1763 return *readpos++;
1764 }
1765 }
1766
1767 static char *
1768 mygzgets (gzFile stream, char *line, int bufsize)
1769 {
1770 char c=0;
1771 char *cpos;
1772
1773 cpos = line;
1774 while (--bufsize > 0 && (c = mygzgetc(stream)) != -1) {
1775 *cpos++ = c;
1776 if (c == '\n')
1777 break;
1778 }
1779 if (c == -1 && cpos == line)
1780 return NULL;
1781 *cpos++ = '\0';
1782 return line;
1783 }
1784
1785
1786 extern char *optarg;
1787 extern int optind, opterr, optopt;
1788
1789 int
1790 main (int argc, char * const argv[])
1791 {
1792 char linebuf[LINEBUFSIZE];
1793 char *line;
1794 char *inputfile = NULL;
1795
1796 const char *text;
1797 const char *idx1;
1798 const char *idx2;
1799 const char *cpos;
1800 int found = 0;
1801 uint32_t csum_prog;
1802 unsigned long lines = 0;
1803 unsigned long rel_line_nr = 0;
1804 char qidbuf[30];
1805 int i;
1806
1807 struct tm *ltime;
1808 struct timeval tv;
1809 time_t ctime, next_ctime, start, end;
1810
1811 LParser *parser;
1812 int opt;
1813
1814 #ifdef DEBUG
1815 smtpd_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1816 qmgr_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1817 filter_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1818 smtpd_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1819 qmgr_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1820 filter_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1821 #endif
1822
1823 if (!(parser = parser_new ())) {
1824 fprintf (stderr, "unable to alloc parser structure\n");
1825 exit (-1);
1826 }
1827
1828 while ((opt = getopt (argc, argv, "f:t:s:e:h:m:q:x:i:l:vgn")) != -1) {
1829 if (opt == 'f') {
1830 parser->from = epool_strdup (&parser->ep, optarg);
1831 } else if (opt == 't') {
1832 parser->to = epool_strdup (&parser->ep, optarg);
1833 } else if (opt == 'v') {
1834 parser->verbose += 1;
1835 } else if (opt == 'g') {
1836 parser->exclude_greylist = 1;
1837 } else if (opt == 'n') {
1838 parser->exclude_ndrs = 1;
1839 } else if (opt == 'h') {
1840 parser->server = epool_strdup (&parser->ep, optarg);
1841 } else if (opt == 'm') {
1842 parser->msgid = epool_strdup (&parser->ep, optarg);
1843 } else if (opt == 'q') {
1844 time_t ltime;
1845 unsigned long rel_line_nr;
1846 MatchList *match = (MatchList *)epool_alloc(&parser->ep, sizeof(MatchList));
1847 if (sscanf(optarg, "T%08lXL%08lX", &ltime, &rel_line_nr) == 2) {
1848 match->mtype = MatchTypeRelLineNr;
1849 match->ltime = ltime;
1850 match->rel_line_nr = rel_line_nr;
1851 match->next = parser->match_list;
1852 parser->match_list = match;
1853 } else {
1854 match->mtype = MatchTypeQID;
1855 match->id = epool_strdup(&parser->ep, optarg);
1856 match->next = parser->match_list;
1857 parser->match_list = match;
1858 }
1859 } else if (opt == 'x') {
1860 parser->strmatch = epool_strdup (&parser->ep, optarg);
1861 } else if (opt == 'i') {
1862 inputfile = optarg;
1863 } else if (opt == 'l') {
1864 char *l;
1865 parser->limit = strtoul (optarg, &l, 0);
1866 if (!*optarg || *l) {
1867 fprintf (stderr, "unable to parse limit '%s'\n", optarg);
1868 exit (-1);
1869 }
1870 } else if (opt == 's') {
1871 // use strptime to convert time
1872 struct tm tm;
1873 char *res;
1874 if ((!(res = strptime (optarg, "%F %T", &tm)) &&
1875 !(res = strptime (optarg, "%s", &tm))) || *res) {
1876 fprintf (stderr, "unable to parse start time\n");
1877 exit (-1);
1878 } else {
1879 parser->start = mkgmtime (&tm);
1880 }
1881 } else if (opt == 'e') {
1882 struct tm tm;
1883 char *res;
1884 if ((!(res = strptime (optarg, "%F %T", &tm)) &&
1885 !(res = strptime (optarg, "%s", &tm))) || *res) {
1886 fprintf (stderr, "unable to parse end time\n");
1887 exit (-1);
1888 } else {
1889 parser->end = mkgmtime (&tm);
1890 }
1891 } else {
1892 print_usage (argv[0]);
1893 exit (-1);
1894 }
1895 }
1896
1897 if (optind < argc) {
1898
1899 if ((argc - optind) > 1) {
1900 print_usage (argv[0]);
1901 exit (-1);
1902 }
1903
1904 char *tmpfn = g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1905
1906 if ((stdout = freopen (tmpfn, "w", stdout)) == NULL) {
1907 perror ("unable to open output file");
1908 exit (-1);
1909 }
1910 if (rename (tmpfn, argv[optind]) != 0) {
1911 perror ("unable to open output file");
1912 unlink (tmpfn);
1913 exit (-1);
1914 }
1915 }
1916
1917 // we use gmtime exerywhere to speedup things, this can cause problems
1918 // when daylight saving time changes
1919
1920 gettimeofday(&tv, NULL);
1921 ltime = localtime (&tv.tv_sec);
1922
1923 if (!parser->start) {
1924 ltime->tm_sec = 0;
1925 ltime->tm_min = 0;
1926 ltime->tm_hour = 0;
1927 parser->start = mkgmtime (ltime);
1928 }
1929
1930 ltime = localtime (&tv.tv_sec);
1931
1932 if (!parser->end) {
1933 parser->end = mkgmtime (ltime);
1934 }
1935
1936 if (parser->end < parser->start) {
1937 fprintf (stderr, "end time before start time\n");
1938 exit (-1);
1939 }
1940
1941 int filecount;
1942 if (inputfile) {
1943 filecount = 1;
1944 } else if ((filecount = parser_count_files (parser)) <= 0) {
1945 fprintf (stderr, "unable to access log files\n");
1946 exit (-1);
1947 }
1948
1949 printf ("# LogReader: %d\n", getpid());
1950
1951 printf ("# Query options\n");
1952 if (parser->from) printf ("# Sender: %s\n", parser->from);
1953 if (parser->to) printf ("# Recipient: %s\n", parser->to);
1954 if (parser->server) printf ("# Server: %s\n", parser->server);
1955 if (parser->msgid) printf ("# MsgID: %s\n", parser->msgid);
1956
1957 MatchList *match = parser->match_list;
1958 while (match) {
1959 if (match->mtype == MatchTypeQID) {
1960 printf ("# QID: %s\n", match->id);
1961 } else if (match->mtype == MatchTypeRelLineNr) {
1962 printf ("# QID: T%08lXL%08lX\n", match->ltime, match->rel_line_nr);
1963 } else {
1964 g_error("internal error - unknown match type %d\n", match->mtype);
1965 }
1966 match = match->next;
1967 }
1968
1969 if (parser->strmatch) printf ("# Match: %s\n", parser->strmatch);
1970
1971 strftime (linebuf, 256, "%F %T", gmtime (&parser->start));
1972 printf ("# Start: %s (%lu)\n", linebuf, parser->start);
1973 strftime (linebuf, 256, "%F %T", gmtime (&parser->end));
1974 printf ("# END: %s (%lu)\n", linebuf, parser->end);
1975 printf ("# End Query Options\n\n");
1976
1977 fflush (stdout);
1978
1979 start = parser->start;
1980 end = parser->end;
1981 ctime = 0;
1982
1983 for (i = filecount - 1; i >= 0; i--) {
1984 gpointer stream;
1985
1986 // syslog files does not conain years, so we must compute then
1987 // cur_year is the assumed start year
1988
1989 cur_month = 0;
1990 cur_year = parser->year[i];
1991
1992 if (i <= 1) {
1993 if (inputfile && strlen(inputfile) == 1 && *inputfile == '-') {
1994 stream = (gpointer) stdin;
1995 } else if (inputfile) {
1996 if (!(stream = (gpointer) fopen (inputfile, "r"))) {
1997 fprintf(stderr, "unable to open log file\n");
1998 exit (-1);
1999 }
2000 } else if (!(stream = (gpointer) fopen (logfiles[i], "r"))) continue;
2001 } else {
2002 if (!(stream = (gpointer) gzopen (logfiles[i], "r"))) continue;
2003 }
2004
2005 while (1) {
2006
2007 if (parser->limit && (parser->count >= parser->limit)) {
2008 printf ("STATUS: aborted by limit (too many hits)\n");
2009 exit (0);
2010 }
2011
2012 if (i <= 1) {
2013 line = fgets (linebuf, LINEBUFSIZE, (FILE *)stream);
2014 } else {
2015 line = mygzgets ((gzFile)stream, linebuf, LINEBUFSIZE);
2016 }
2017
2018 if (!line) break;
2019
2020 int len = strlen (line);
2021 int pid = 0;
2022
2023 cpos = line;
2024
2025 next_ctime = parse_time (&cpos, len);
2026
2027 if (!next_ctime) {
2028 continue;
2029 }
2030
2031 if (next_ctime != ctime) {
2032 rel_line_nr = 0;
2033 } else {
2034 rel_line_nr++;
2035 }
2036
2037 ctime = next_ctime;
2038
2039 if (ctime < start) continue;
2040 if (ctime > end) break;
2041
2042 lines++;
2043
2044 found = 0; while (!isspace (*cpos)) { cpos++; found = 1; }
2045 if (!found) {
2046 debug_error ("missing hostname", line);
2047 continue;
2048 }
2049
2050 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
2051 if (!found) {
2052 debug_error ("missing spaces after host", line);
2053 continue;
2054 }
2055
2056 if ((*cpos == 'l') && !strncmp (cpos, "last message repeated", 21)) {
2057 continue;
2058 }
2059
2060 //printf ("LINE: %s\n", line);
2061 //const char *prog = cpos;
2062
2063 csum_prog = 0;
2064 found = 0; while (*cpos && (*cpos != ':') && (*cpos != '[')) {
2065 csum_prog = ((csum_prog << 8)|(csum_prog >> 24)) + *cpos;
2066 cpos++;
2067 found++;
2068 }
2069
2070 //idx1 = g_strndup (prog, found);
2071 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
2072 //free (idx1);
2073
2074 if (*cpos == '[') {
2075 cpos++;
2076 found = 0; while (isdigit (*cpos)) {
2077 pid = pid*10 + *cpos - '0';
2078 cpos++;
2079 found++;
2080 }
2081 if (found < 1 || found > 15 || *cpos != ']') {
2082 debug_error ("unable to parse pid", line);
2083 continue;
2084 }
2085 cpos++;
2086 }
2087
2088 if (*cpos++ != ':') {
2089 debug_error ("missing collon", line);
2090 continue;
2091 }
2092
2093
2094 if (!isspace (*cpos++)) {
2095 debug_error ("missing space after collon", line);
2096 continue;
2097 }
2098
2099 text = cpos;
2100
2101 parser->ctime = ctime;
2102
2103 int strmatch = 0;
2104
2105 if (parser->strmatch && STRMATCH(parser->strmatch, text)) {
2106 strmatch = 1;
2107 }
2108
2109 if ((csum_prog == PROXPROX) ||
2110 (csum_prog == PMG_SMTP_FILTER)) {
2111
2112 if ((idx1 = parse_qid (&cpos, qidbuf, ':', 25))) {
2113
2114 FEntry *fe;
2115
2116 if (!(fe = fentry_get (parser, idx1))) {
2117 continue;
2118 }
2119
2120 loglist_add (&fe->ep, &fe->loglist, line, len, lines);
2121
2122 if (strmatch) fe->strmatch = 1;
2123
2124 //fixme: BCC, Notify?
2125 //fixme: parse virus info
2126 //fixme: parse spam score
2127
2128 if ((*cpos == 'a') && !strncmp (cpos, "accept mail to <", 16)) {
2129
2130 const char *to_s, *to_e;
2131
2132 to_s = cpos = cpos + 16;
2133
2134 while (*cpos && (*cpos != '>')) { cpos++; }
2135
2136 if (*cpos != '>') continue;
2137
2138 to_e = cpos;
2139
2140 cpos++;
2141
2142 if ((*cpos++ != ' ') || (*cpos++ != '(')) continue;
2143
2144 if (!(idx1 = parse_qid (&cpos, qidbuf, ')', 15))) continue;
2145
2146 // parser_track (parser, idx1, 1);
2147
2148 fentry_tolist_add (fe, 'A', to_s, to_e - to_s, idx1, strlen (idx1));
2149
2150 } else if ((*cpos == 'm') && !strncmp (cpos, "moved mail for <", 16)) {
2151 const char *to_s, *to_e;
2152
2153 to_s = cpos = cpos + 16;
2154
2155 while (*cpos && (*cpos != '>')) { cpos++; }
2156
2157 to_e = cpos;
2158
2159 if (strncmp (cpos, "> to ", 5)) continue;
2160 cpos += 5;
2161
2162 if (!strncmp (cpos, "spam", 4)) {
2163 cpos += 4;
2164 } else if (!strncmp (cpos, "virus", 5)) {
2165 cpos += 5;
2166 } else {
2167 continue;
2168 }
2169
2170 if (strncmp (cpos, " quarantine - ", 14)) continue;
2171 cpos += 14;
2172
2173 if (!(idx1 = parse_qid (&cpos, qidbuf, 0, 25))) continue;
2174
2175 fentry_tolist_add (fe, 'Q', to_s, to_e - to_s, idx1, strlen (idx1));
2176
2177 } else if ((*cpos == 'b') && !strncmp (cpos, "block mail to <", 15)) {
2178
2179 const char *to_s;
2180
2181 to_s = cpos = cpos + 15;
2182
2183 while (*cpos && (*cpos != '>')) { cpos++; }
2184
2185 if (*cpos != '>') continue;
2186
2187 fentry_tolist_add (fe, 'B', to_s, cpos - to_s, NULL, 0);
2188
2189 } else if ((*cpos == 'p') && !strncmp (cpos, "processing time: ", 17)) {
2190 cpos += 17;
2191
2192 sscanf (cpos, "%f", &fe->ptime);
2193
2194 fe->finished = 1;
2195 }
2196
2197 }
2198
2199 } else if (csum_prog == POSTFIX_POSTSCREEN) {
2200
2201 SEntry *se;
2202
2203 if (!pid) {
2204 debug_error ("no pid for postscreen", line);
2205 continue;
2206 }
2207
2208
2209 if ((*text == 'N') && !strncmp (text, "NOQUEUE: reject: RCPT from ", 27)) {
2210
2211 cpos = text + 27;
2212
2213 if (!(idx1 = strstr (cpos, "; client ["))) continue;
2214
2215 const char *client = cpos = idx1 + 10;
2216
2217 while (*cpos && (*cpos != ']')) { cpos++; }
2218
2219 const char *client_end = cpos;
2220
2221 if (!(idx1 = strstr (cpos, "; from=<"))) continue;
2222
2223 const char *from = cpos = idx1 + 8;
2224
2225 while (*cpos && (*cpos != '>')) { cpos++; }
2226 idx1 = cpos;
2227
2228 if ((*cpos == '>') && strncmp (cpos, ">, to=<", 7)) continue;
2229
2230 const char *to = cpos = cpos + 7;
2231
2232 while (*cpos && (*cpos != '>')) { cpos++; }
2233
2234 if (*cpos != '>') continue;
2235
2236 if (!(se = sentry_get (parser, pid, ctime, rel_line_nr))) {
2237 continue;
2238 }
2239
2240 if (strmatch) se->strmatch = 1;
2241
2242 loglist_add (&se->ep, &se->loglist, line, len, lines);
2243
2244 sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, 'N');
2245
2246 sentry_set_connect (se, client, client_end - client);
2247
2248 g_hash_table_remove (parser->smtpd_h, &se->pid);
2249
2250 se->disconnect = 1;
2251
2252 sentry_print (parser, se);
2253 sentry_free (parser, se);
2254 }
2255
2256 } else if (csum_prog == POSTFIX_QMGR) {
2257
2258 if ((idx2 = text) && (idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2259
2260 QEntry *qe;
2261
2262 if (!(qe = qentry_get (parser, idx1))) {
2263 continue;
2264 }
2265
2266 if (strmatch) qe->strmatch = 1;
2267
2268 qe->cleanup = 1;
2269
2270 loglist_add (&qe->ep, &qe->loglist, line, len, lines);
2271
2272 if ((*idx2 == 'f') && !strncmp (idx2, "from=<", 6)) {
2273
2274 cpos = idx2 = idx2 + 6;
2275 while (*cpos && (*cpos != '>')) { cpos++; }
2276
2277 if (*cpos != '>') {
2278 debug_error ("unable to parse 'from' address", line);
2279 continue;
2280 }
2281
2282 qentry_set_from (qe, idx2, cpos - idx2);
2283
2284 cpos++;
2285
2286 if ((*cpos == ',') && !strncmp (cpos, ", size=", 7)) {
2287 int size = 0;
2288 cpos += 7;
2289
2290 while (isdigit (*cpos)) { size = size*10 + *cpos++ - '0'; }
2291
2292 qe->size = size;
2293 }
2294
2295 } else if ((*idx2 == 'r') && !strncmp (idx2, "removed\n", 8)) {
2296
2297 qe->removed = 1;
2298
2299 qentry_finalize (parser, qe);
2300 }
2301 }
2302
2303 } else if ((csum_prog == POSTFIX_SMTP) ||
2304 (csum_prog == POSTFIX_LMTP) ||
2305 (csum_prog == POSTFIX_LOCAL) ||
2306 (csum_prog == POSTFIX_ERROR)) {
2307
2308 int lmtp = (csum_prog == POSTFIX_LMTP);
2309
2310 if ((cpos = text) && (idx1 = parse_qid (&cpos, qidbuf, ':', 15))) {
2311
2312 QEntry *qe;
2313
2314 if (!(qe = qentry_get (parser, idx1))) {
2315 continue;
2316 }
2317
2318 if (strmatch) qe->strmatch = 1;
2319
2320 qe->cleanup = 1;
2321
2322 loglist_add (&qe->ep, &qe->loglist, line, len, lines);
2323
2324 if (strncmp (cpos, "to=<", 4)) continue;
2325 cpos += 4;
2326
2327 const char *to_s, *to_e;
2328
2329 to_s = cpos;
2330
2331 while (*cpos && (*cpos != '>')) { cpos++; }
2332
2333 if (*cpos != '>') continue;
2334
2335 to_e = cpos;
2336
2337 cpos ++;
2338
2339 if (!(cpos = strstr (cpos, ", relay="))) continue;
2340 cpos += 8;
2341
2342 const char *relay_s, *relay_e;
2343
2344 relay_s = cpos;
2345
2346 while (*cpos && (*cpos != ',')) { cpos++; }
2347
2348 if (*cpos != ',') continue;
2349
2350 relay_e = cpos;
2351
2352 if (!(idx1 = strstr (cpos + 1, ", dsn="))) continue;
2353
2354 cpos = idx1 + 6;
2355
2356 if (!isdigit (*cpos)) continue;
2357
2358 char dstatus = *cpos;
2359
2360 qentry_tolist_add (qe, ctime, dstatus, to_s, to_e - to_s,
2361 relay_s, relay_e - relay_s);
2362
2363 if (!lmtp) continue; // filter always uses lmtp
2364
2365 if (!(idx1 = strstr (cpos + 1, "status=sent (250 2.")))
2366 continue;
2367
2368 cpos = idx1 = idx1 + 19;
2369
2370 if (*cpos == '5' && (idx1 = strstr (cpos, "5.0 OK ("))) {
2371 cpos = idx1 = idx1 + 8;
2372 } else if (*cpos == '7' && (idx1 = strstr (cpos, "7.0 BLOCKED ("))) {
2373 cpos = idx1 = idx1 + 13;
2374 } else {
2375 continue;
2376 }
2377
2378 if (!(idx1 = parse_qid (&cpos, qidbuf, ')', 25)))
2379 continue;
2380
2381 FEntry *fe;
2382
2383 qe->filtered = 1;
2384
2385 if ((fe = g_hash_table_lookup (parser->filter_h, idx1))) {
2386 qe->filter = fe;
2387 }
2388 }
2389
2390 } else if (csum_prog == POSTFIX_SMTPD) {
2391 SEntry *se;
2392
2393 if (!pid) {
2394 debug_error ("no pid for smtpd", line);
2395 continue;
2396 }
2397
2398 if (!(se = sentry_get (parser, pid, ctime, rel_line_nr))) {
2399 continue;
2400 }
2401
2402 if (strmatch) se->strmatch = 1;
2403
2404 loglist_add (&se->ep, &se->loglist, line, len, lines);
2405
2406 if ((*text == 'c') && !strncmp (text, "connect from ", 13)) {
2407
2408 cpos = idx1 = text + 13;
2409
2410 while (*idx1 && !isspace (*idx1)) { idx1++; }
2411
2412 sentry_set_connect (se, cpos, idx1 - cpos);
2413
2414 // fixme: do we need this?
2415 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2416 // se->external = 1;
2417 //}
2418
2419 } else if ((*text == 'd') && !strncmp (text, "disconnect from", 15)) {
2420
2421 // unlink
2422 g_hash_table_remove (parser->smtpd_h, &se->pid);
2423
2424 se->disconnect = 1;
2425
2426 if (sentry_ref_rem_unneeded (parser, se) == 0) {
2427 sentry_print (parser, se);
2428 sentry_free (parser, se);
2429 } else {
2430 sentry_ref_finalize (parser, se);
2431 }
2432
2433 } else if ((*text == 'N') && !strncmp (text, "NOQUEUE:", 8)) {
2434
2435 cpos = text + 8;
2436
2437 // parse 'whatsup' (reject:)
2438 while (*cpos && (*cpos != ':')) { cpos++; }
2439 if (*cpos != ':') continue;
2440 cpos++;
2441
2442 // parse '%s from %s:'
2443 while (*cpos && (*cpos != ':')) { cpos++; }
2444 if (*cpos != ':') continue;
2445 idx1 = cpos++;
2446
2447 // parse '%s;'
2448 while (*cpos && (*cpos != ';')) { cpos++; }
2449 if (*cpos != ';') continue;
2450
2451 // greylisting test
2452 char dstatus = 'N';
2453 *(char *)cpos = 0; // dangerous hack: delimit string
2454 if (strstr (idx1, ": Recipient address rejected: Service is unavailable (try later)")) {
2455 dstatus = 'G';
2456 }
2457 *(char *)cpos = ';'; // dangerous hack: restore line
2458
2459 if (!(idx1 = strstr (cpos, "; from=<"))) continue;
2460
2461 const char *from = cpos = idx1 + 8;
2462
2463 while (*cpos && (*cpos != '>')) { cpos++; }
2464 idx1 = cpos;
2465
2466 if ((*cpos == '>') && strncmp (cpos, "> to=<", 6)) continue;
2467
2468 const char *to = cpos = cpos + 6;
2469
2470 while (*cpos && (*cpos != '>')) { cpos++; }
2471
2472 if (*cpos != '>') continue;
2473
2474 sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, dstatus);
2475
2476 } else if ((idx2 = text) && (idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2477
2478 QEntry *qe;
2479
2480 if ((qe = qentry_get (parser, idx1))) {
2481
2482 if (strmatch) qe->strmatch = 1;
2483
2484 sentry_ref_add (se, qe);
2485
2486 if ((*idx2 == 'c') && !strncmp (idx2, "client=", 7)) {
2487 cpos = idx2 = idx2 + 7;
2488
2489 while (*cpos && !isspace (*cpos)) { cpos++; }
2490
2491 qentry_set_client (qe, idx2, cpos - idx2);
2492 }
2493 }
2494
2495 }
2496
2497 } else if (csum_prog == POSTFIX_CLEANUP) { // postfix/cleanup
2498
2499 QEntry *qe;
2500
2501 idx2 = text;
2502 if ((idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2503 if ((qe = qentry_get (parser, idx1))) {
2504
2505 if (strmatch) qe->strmatch = 1;
2506
2507 loglist_add (&qe->ep, &qe->loglist, line, len, lines);
2508
2509 if ((*idx2 == 'm') && !strncmp (idx2, "message-id=", 11)) {
2510
2511 cpos = idx2 = idx2 + 11;
2512
2513 while (*cpos && !isspace(*cpos)) { cpos++; }
2514
2515 qentry_set_msgid (qe, idx2, cpos - idx2);
2516
2517 qe->cleanup = 1;
2518 }
2519 }
2520 }
2521 }
2522 }
2523
2524 if (i <= 1) {
2525 fclose ((FILE *)stream);
2526 } else {
2527 gzclose ((gzFile)stream);
2528 }
2529
2530 if (ctime > end) break;
2531 }
2532
2533
2534 #ifdef DEBUG
2535 printf ("LINES: %d\n", lines);
2536 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser->smtpd_h));
2537 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser->qmgr_h));
2538 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser->filter_h));
2539
2540 printf ("MEMDEB SMTPD entries: %d %d\n",
2541 g_hash_table_size (smtpd_debug_alloc),
2542 g_hash_table_size (smtpd_debug_free));
2543 printf ("MEMDEB QMGR entries: %d %d\n",
2544 g_hash_table_size (qmgr_debug_alloc),
2545 g_hash_table_size (qmgr_debug_free));
2546 printf ("MEMDEB FILTER entries: %d %d\n",
2547 g_hash_table_size (filter_debug_alloc),
2548 g_hash_table_size (filter_debug_free));
2549 #endif
2550
2551 g_hash_table_foreach (parser->qmgr_h, qentry_cleanup_hash, parser);
2552 g_hash_table_foreach (parser->smtpd_h, sentry_cleanup_hash, parser);
2553 g_hash_table_foreach (parser->filter_h, fentry_cleanup_hash, parser);
2554
2555 #ifdef DEBUG
2556 printf ("MEMDEB SMTPD entries: %d %d\n",
2557 g_hash_table_size (smtpd_debug_alloc),
2558 g_hash_table_size (smtpd_debug_free));
2559 printf ("MEMDEB QMGR entries: %d %d\n",
2560 g_hash_table_size (qmgr_debug_alloc),
2561 g_hash_table_size (qmgr_debug_free));
2562 printf ("MEMDEB FILTER entries: %d %d\n",
2563 g_hash_table_size (filter_debug_alloc),
2564 g_hash_table_size (filter_debug_free));
2565
2566 g_hash_table_foreach (smtpd_debug_alloc, sentry_debug_alloc, parser);
2567
2568 #endif
2569
2570
2571 #ifdef EPOOL_DEBUG
2572 printf ("MEMMAX %d\n", ep_maxalloc);
2573 #endif
2574
2575 parser_free (parser);
2576
2577 fclose (stdout);
2578
2579 exit (0);
2580 }