initial release

This commit is contained in:
SJ
2011-11-14 15:57:52 +01:00
commit 57f172c1b1
58 changed files with 13880 additions and 0 deletions

82
src/Makefile Normal file
View File

@ -0,0 +1,82 @@
SHELL = /bin/sh
prefix = /usr/local
exec_prefix = ${prefix}
bindir = ${exec_prefix}/bin
sbindir = ${exec_prefix}/sbin
includedir = ${prefix}/include
libdir = ${exec_prefix}/lib
libexecdir = ${exec_prefix}/libexec
srcdir = .
sysconfdir = ${prefix}/etc
mandir = ${datarootdir}/man
datarootdir = ${prefix}/share
localstatedir = /var
CC = gcc
CFLAGS = -O2 -Wall -g
DEFS = -D_GNU_SOURCE -DHAVE_ANTIVIRUS -DHAVE_CLAMD -DHAVE_TRE -DNEED_MYSQL -DNEED_SQLITE3
INCDIR = -I. -I.. -I/usr/local/mysql/include -fPIC -g -static-libgcc -fno-omit-frame-pointer -m32 -fPIC -g -static-libgcc -fno-omit-frame-pointer -fno-strict-aliasing -DMY_PTHREAD_FASTMUTEX=1
LIBDIR = -L.
LIBS = -lz -lm -ldl -lcrypto -ltre -lsqlite3 -lpthread -L/usr/local/mysql/lib -lmysqlclient_r -lpthread -lm -lrt -ldl -lguide
OBJS = dirs.o misc.o counters.o cfg.o sig.o decoder.o list.o boundary.o parser.o parser_utils.o session.o message.o digest.o store.o tai.o avir.o clamd.o
MYSQL_OBJS =
RUNNING_USER = piler
RUNNING_GROUP = `id -gn $(RUNNING_USER)`
PILER_VERSION=0
PILER_REVISION=1
PILER_RELEASE=1
LIBPILER_VERSION=$(PILER_VERSION).$(PILER_REVISION).$(PILER_RELEASE)
MAKE = `which make`
INSTALL = /bin/ginstall -c
all: libpiler.a piler pilerconf test
install: install-piler
piler: piler.c libpiler.a
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ piler.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR)
libpiler.a: $(OBJS) $(MYSQL_OBJS)
ar cr libpiler.a $(OBJS) $(MYSQL_OBJS)
ranlib libpiler.a
$(CC) -shared -Wl -o libpiler.so.$(LIBPILER_VERSION) $(OBJS) $(MYSQL_OBJS) $(LIBS) $(LDAP_LIBS)
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION)
pilerconf: pilerconf.c cfg.o misc.o tai.o
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $^ $(LIBS) $(LIBDIR)
test:
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o piletest $(srcdir)/test.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR)
%.o: $(srcdir)/%.c
$(CC) $(CFLAGS) -fPIC $(INCDIR) $(DEFS) -c $< -o $@
install-piler:
$(INSTALL) -d $(DESTDIR)$(bindir)
$(INSTALL) -d $(DESTDIR)$(sbindir)
$(INSTALL) -d $(DESTDIR)$(libdir)
$(INSTALL) -m 0644 libpiler.a $(DESTDIR)$(libdir)
$(INSTALL) -m 0755 libpiler.so.$(LIBPILER_VERSION) $(DESTDIR)$(libdir)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION))
$(INSTALL) -d $(DESTDIR)$(libexecdir)/piler
$(INSTALL) -d $(DESTDIR)$(datarootdir)/piler
$(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir)
$(INSTALL) -m 0755 pilerconf $(DESTDIR)$(sbindir)
clean:
rm -f *.o *.a libpiler.so* piler pilerconf piletest
distclean: clean
rm -f Makefile

82
src/Makefile.in Normal file
View File

@ -0,0 +1,82 @@
SHELL = @SHELL@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
sbindir = @sbindir@
includedir = @includedir@
libdir = @libdir@
libexecdir = @libexecdir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
mandir = @mandir@
datarootdir = @datarootdir@
localstatedir = @localstatedir@
CC = @CC@
CFLAGS = @CFLAGS@ @CPPFLAGS@
DEFS = @defs@
INCDIR = -I. -I.. @INCDIR@ @mysql_includes@
LIBDIR = -L. @LIBDIR@ @LDFLAGS@
LIBS = @LIBS@ @mysql_libs@
OBJS = @OBJS@
MYSQL_OBJS = @mysql_obj@
RUNNING_USER = @RUNNING_USER@
RUNNING_GROUP = `@id_bin@ -gn $(RUNNING_USER)`
PILER_VERSION=0
PILER_REVISION=1
PILER_RELEASE=1
LIBPILER_VERSION=$(PILER_VERSION).$(PILER_REVISION).$(PILER_RELEASE)
MAKE = `which make`
INSTALL = @INSTALL@
all: libpiler.a piler pilerconf test
install: install-piler
piler: piler.c libpiler.a
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ piler.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR) @LDFLAGS@ @libclamav_extra_libs@
libpiler.a: $(OBJS) $(MYSQL_OBJS)
ar cr libpiler.a $(OBJS) $(MYSQL_OBJS)
ranlib libpiler.a
$(CC) -shared -Wl -o libpiler.so.$(LIBPILER_VERSION) $(OBJS) $(MYSQL_OBJS) $(LIBS) $(LDAP_LIBS) @LDFLAGS@
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION)
pilerconf: pilerconf.c cfg.o misc.o tai.o
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $^ $(LIBS) $(LIBDIR)
test:
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o piletest $(srcdir)/test.c -lpiler $(LIBS) $(LDAP_LIBS) $(LIBDIR) @LDFLAGS@
%.o: $(srcdir)/%.c
$(CC) $(CFLAGS) -fPIC $(INCDIR) $(DEFS) -c $< -o $@
install-piler:
$(INSTALL) -d $(DESTDIR)$(bindir)
$(INSTALL) -d $(DESTDIR)$(sbindir)
$(INSTALL) -d $(DESTDIR)$(libdir)
$(INSTALL) -m 0644 libpiler.a $(DESTDIR)$(libdir)
$(INSTALL) -m 0755 libpiler.so.$(LIBPILER_VERSION) $(DESTDIR)$(libdir)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so)
(cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION))
$(INSTALL) -d $(DESTDIR)$(libexecdir)/piler
$(INSTALL) -d $(DESTDIR)$(datarootdir)/piler
$(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir)
$(INSTALL) -m 0755 pilerconf $(DESTDIR)$(sbindir)
clean:
rm -f *.o *.a libpiler.so* piler pilerconf piletest
distclean: clean
rm -f Makefile

79
src/av.h Normal file
View File

@ -0,0 +1,79 @@
/*
* av.h, SJ
*/
#ifndef _AV_H
#define _AV_H
#include "defs.h"
#include "cfg.h"
#define AV_OK 200
#define AV_VIRUS 403
#define AV_ERROR 501
// clamd stuff
#define CLAMD_RESP_CLEAN "OK"
#define CLAMD_RESP_INFECTED "FOUND"
#define CLAMD_RESP_ERROR "ERROR"
int clamd_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
int clamd_net_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// Dr.Web stuff
#define DRWEB_RESP_VIRUS 0x20
#define DRWEB_VIRUS_HAS_FOUND_MESSAGE "Virus has been found in message. See drwebd.log for details"
int drweb_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// avast! stuff
#define AVAST_READY "220"
#define AVAST_CMD_QUIT "QUIT\r\n"
#define AVAST_RESP_OK "200"
#define AVAST_RESP_ENGINE_ERROR "451"
#define AVAST_RESP_SYNTAX_ERROR "501"
#define AVAST_RESP_CLEAN "[+]"
#define AVAST_RESP_INFECTED "[L]"
int avast_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
int avast_cmd_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// Kaspersky stuff
#define KAV_CMD_QUIT "QUIT\r\n"
#define KAV_READY "201 "
#define KAV_RESP_CLEAN "220 File is clean"
#define KAV_RESP_INFECTED "230 File is infected"
#define KAV_RESP_INFECTED_NAME "322-"
#define KAV_RESP_NOT_FOUND "525 File not found"
int kav_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
// avg stuff
#define AVG_READY "220"
#define AVG_CMD_QUIT "QUIT\r\n"
#define AVG_RESP_OK "200"
#define AVG_RESP_VIRUS "403"
#define AVG_RESP_NOT_FOUND "404"
#define AVG_RESP_ERROR "501"
#define AVG_NOT_FOUND 404
int avg_scan(char *tmpdir, char *tmpfile, char *engine, char *avinfo, struct __config *cfg);
int moveMessageToQuarantine(struct session_data *sdata, struct __config *cfg);
void sendNotificationToPostmaster(struct session_data *sdata, char *rcpttoemail, char *fromemail, char *virusinfo, char *avengine, struct __config *cfg);
#endif /* _AV_H */

55
src/avir.c Normal file
View File

@ -0,0 +1,55 @@
/*
* avir.c, SJ
*/
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <piler.h>
int do_av_check(struct session_data *sdata, char *rcpttoemail, char *fromemail, char *virusinfo, struct __data *data, struct __config *cfg){
int rav = AVIR_OK;
char avengine[SMALLBUFSIZE];
if(sdata->need_scan == 0) return rav;
memset(avengine, 0, SMALLBUFSIZE);
#ifdef HAVE_LIBCLAMAV
const char *virname;
unsigned int options=0;
options = CL_SCAN_STDOPT | CL_SCAN_ARCHIVE | CL_SCAN_MAIL | CL_SCAN_OLE2;
if(cfg->use_libclamav_block_max_feature == 1) options |= CL_SCAN_BLOCKMAX;
if(cfg->clamav_block_encrypted_archives == 1) options |= CL_SCAN_BLOCKENCRYPTED;
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: trying to pass to libclamav", sdata->ttmpfile);
if(cl_scanfile(sdata->ttmpfile, &virname, NULL, data->engine, options) == CL_VIRUS){
memset(virusinfo, 0, SMALLBUFSIZE);
strncpy(virusinfo, virname, SMALLBUFSIZE-1);
rav = AVIR_VIRUS;
snprintf(avengine, SMALLBUFSIZE-1, "libClamAV");
}
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: virus info: '%s'", sdata->ttmpfile, virname);
#endif
#ifdef HAVE_CLAMD
if(strlen(cfg->clamd_addr) > 3 && cfg->clamd_port > 0){
if(clamd_net_scan(sdata->ttmpfile, avengine, virusinfo, cfg) == AV_VIRUS) rav = AVIR_VIRUS;
} else {
if(clamd_scan(sdata->ttmpfile, avengine, virusinfo, cfg) == AV_VIRUS) rav = AVIR_VIRUS;
}
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: done virus scanning", sdata->ttmpfile);
return rav;
}

98
src/boundary.c Normal file
View File

@ -0,0 +1,98 @@
/*
* boundary.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <piler.h>
/*
* append something to the boundary list if we have to
*/
int append_boundary(struct boundary **boundaries, char *p){
struct boundary *q, *t, *u=NULL;
q = *boundaries;
while(q){
if(strcmp(q->boundary_str, p) == 0)
return 0;
u = q;
q = q->r;
}
t = new_boundary(p);
if(t){
if(*boundaries == NULL)
*boundaries = t;
else if(u)
u->r = t;
return 1;
}
return -1;
}
/*
* create a new boundary structure
*/
struct boundary *new_boundary(char *s){
struct boundary *h=NULL;
if((h = malloc(sizeof(struct boundary))) == NULL)
return NULL;
strncpy(h->boundary_str, s, BOUNDARY_LEN-1);
h->r = NULL;
return h;
}
/*
* is this a boundary string?
*/
int is_boundary(struct boundary *boundaries, char *s){
struct boundary *p;
p = boundaries;
while(p != NULL){
if(strstr(s, p->boundary_str)) return 1;
p = p->r;
}
return 0;
}
/*
* free boundary list
*/
void free_boundary(struct boundary *b){
struct boundary *p;
while(b){
p = b->r;
//printf("free boundary: %s\n", b->boundary_str);
if(b)
free(b);
b = p;
}
}

14
src/boundary.h Normal file
View File

@ -0,0 +1,14 @@
/*
* boundary.h, SJ
*/
#ifndef _BOUNDARY_H
#define _BOUNDARY_H
int append_boundary(struct boundary **boundaries, char *p);
struct boundary *new_boundary(char *s);
int is_boundary(struct boundary *boundaries, char *s);
void free_boundary(struct boundary *b);
#endif /* _LIST_H */

282
src/cfg.c Normal file
View File

@ -0,0 +1,282 @@
/*
* cfg.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "cfg.h"
#include "misc.h"
#include "config.h"
int string_parser(char *src, char *target, int limit){
snprintf(target, limit, "%s", src);
return 0;
};
int multi_line_string_parser(char *src, char *target, int limit){
if(strlen(src) > 0 && strlen(target) + strlen(src) + 3 < limit){
strncat(target, src, limit);
strncat(target, "\r\n", limit);
return 0;
}
return 1;
};
int int_parser(char *src, int *target, int limit){
*target = strtol(src, (char **) NULL, 10);
return 0;
};
int float_parser(char *src, float *target, int limit){
*target = strtof(src, (char **) NULL);
return 0;
};
struct _parse_rule {
char *name;
char *type;
int(*parser)(char*,void*,int);
size_t offset;
char *def_val;
int limit;
};
/*
* all known configuration items in order
*/
struct _parse_rule config_parse_rules[] =
{
{ "backlog", "integer", (void*) int_parser, offsetof(struct __config, backlog), "20", sizeof(int)},
{ "clamd_addr", "string", (void*) string_parser, offsetof(struct __config, clamd_addr), "", MAXVAL-1},
{ "clamd_port", "integer", (void*) int_parser, offsetof(struct __config, clamd_port), "0", sizeof(int)},
{ "clamd_socket", "string", (void*) string_parser, offsetof(struct __config, clamd_socket), CLAMD_SOCKET, MAXVAL-1},
{ "deferdir", "string", (void*) string_parser, offsetof(struct __config, deferdir), DEFER_DIR, MAXVAL-1},
{ "hostid", "string", (void*) string_parser, offsetof(struct __config, hostid), HOSTID, MAXVAL-1},
{ "iv", "string", (void*) string_parser, offsetof(struct __config, iv), "", MAXVAL-1},
{ "listen_addr", "string", (void*) string_parser, offsetof(struct __config, listen_addr), "127.0.0.1", MAXVAL-1},
{ "listen_port", "integer", (void*) int_parser, offsetof(struct __config, listen_port), "10025", sizeof(int)},
{ "locale", "string", (void*) string_parser, offsetof(struct __config, locale), "", MAXVAL-1},
{ "max_connections", "integer", (void*) int_parser, offsetof(struct __config, max_connections), "30", sizeof(int)},
{ "max_requests_per_child", "integer", (void*) int_parser, offsetof(struct __config, max_requests_per_child), "200", sizeof(int)},
{ "memcached_servers", "string", (void*) string_parser, offsetof(struct __config, memcached_servers), "127.0.0.1", MAXVAL-1},
{ "memcached_ttl", "integer", (void*) int_parser, offsetof(struct __config, memcached_ttl), "86400", sizeof(int)},
{ "mysqlhost", "string", (void*) string_parser, offsetof(struct __config, mysqlhost), "", MAXVAL-1},
{ "mysqlport", "integer", (void*) int_parser, offsetof(struct __config, mysqlport), "", sizeof(int)},
{ "mysqlsocket", "string", (void*) string_parser, offsetof(struct __config, mysqlsocket), "/tmp/mysql.sock", MAXVAL-1},
{ "mysqluser", "string", (void*) string_parser, offsetof(struct __config, mysqluser), "clapf", MAXVAL-1},
{ "mysqlpwd", "string", (void*) string_parser, offsetof(struct __config, mysqlpwd), "", MAXVAL-1},
{ "mysqldb", "string", (void*) string_parser, offsetof(struct __config, mysqldb), "clapf", MAXVAL-1},
{ "mysql_connect_timeout", "integer", (void*) int_parser, offsetof(struct __config, mysql_connect_timeout), "2", sizeof(int)},
{ "number_of_worker_processes", "integer", (void*) int_parser, offsetof(struct __config, number_of_worker_processes), "10", sizeof(int)},
{ "pidfile", "string", (void*) string_parser, offsetof(struct __config, pidfile), PIDFILE, MAXVAL-1},
{ "piler_header_field", "string", (void*) string_parser, offsetof(struct __config, piler_header_field), "", MAXVAL-1},
{ "queuedir", "string", (void*) string_parser, offsetof(struct __config, queuedir), QUEUE_DIR, MAXVAL-1},
{ "session_timeout", "integer", (void*) int_parser, offsetof(struct __config, session_timeout), "420", sizeof(int)},
{ "sqlite3_pragma", "string", (void*) string_parser, offsetof(struct __config, sqlite3_pragma), "", MAXVAL-1},
{ "username", "string", (void*) string_parser, offsetof(struct __config, username), "piler", MAXVAL-1},
{ "use_antivirus", "integer", (void*) int_parser, offsetof(struct __config, use_antivirus), "1", sizeof(int)},
{ "verbosity", "integer", (void*) int_parser, offsetof(struct __config, verbosity), "1", sizeof(int)},
{ "workdir", "string", (void*) string_parser, offsetof(struct __config, workdir), WORK_DIR, MAXVAL-1},
{NULL, NULL, NULL, 0, 0}
};
/*
* parse configfile
*/
int parse_config_file(char *configfile, struct __config *target_cfg, struct _parse_rule *rules){
char line[MAXVAL], *chpos;
FILE *f;
if(!configfile) return -1;
f = fopen(configfile, "r");
if(!f) return -1;
while(fgets(&line[0], MAXVAL-1, f)){
if(line[0] == ';' || line[0] == '#') continue;
chpos = strchr(line, '=');
if(chpos){
trimBuffer(chpos+1);
*chpos = '\0';
int i = 0;
while(rules[i].name){
if(!strcmp(line, rules[i].name)) {
if(rules[i].parser(chpos+1, (char*)target_cfg + rules[i].offset, rules[i].limit)){
printf("failed to parse %s: \"%s\"\n", line, chpos+1);
}
break;
}
i++;
}
if(!rules[i].name) syslog(LOG_PRIORITY, "unknown key: \"%s\"", line);
}
}
fclose(f);
return 0;
}
int load_default_config(struct __config *cfg, struct _parse_rule *rules){
int i=0;
while(rules[i].name){
rules[i].parser(rules[i].def_val, (char*)cfg + rules[i].offset, rules[i].limit);
i++;
}
return 0;
}
/*
* read configuration file variables
*/
struct __config read_config(char *configfile){
struct __config cfg;
/* reset config structure and fill it with defaults */
memset((char *)&cfg, 0, sizeof(struct __config));
load_default_config(&cfg, config_parse_rules);
/* parse the config file */
if(parse_config_file(configfile, &cfg, config_parse_rules) == -1) printf("error parsing the configfile: %s\n", configfile);
return cfg;
}
/*
* print a single configuration item as key=value
*/
void print_config_item(struct __config *cfg, struct _parse_rule *rules, int i){
int j;
float f;
char *p, buf[MAXVAL];
p = (char*)cfg + rules[i].offset;
if(strcmp(rules[i].type, "integer") == 0){
memcpy((char*)&j, p, sizeof(int));
printf("%s=%d\n", rules[i].name, j);
}
else if(strcmp(rules[i].type, "float") == 0){
memcpy((char*)&f, p, sizeof(float));
printf("%s=%.4f\n", rules[i].name, f);
}
else if(strcmp(rules[i].type, "multi_line_string") == 0){
j = 0;
do {
p = split_str(p, "\r\n", buf, MAXVAL-1);
if(p || !j) printf("%s=%s\n", rules[i].name, buf);
j++;
} while(p);
}
else {
trimBuffer(p);
printf("%s=%s\n", rules[i].name, p);
}
}
/*
* print all known configuration items
*/
void print_config_all(struct __config *cfg, char *key){
int i=0;
struct _parse_rule *rules;
rules = &config_parse_rules[0];
while(rules[i].name){
if(key == NULL){
print_config_item(cfg, rules, i);
}
else {
if(strcmp(key, rules[i].name) == 0)
print_config_item(cfg, rules, i);
}
i++;
}
}
/*
* print all configuration items found in configfile
*/
void print_config(char *configfile, struct __config *cfg){
FILE *f;
char line[MAXVAL], *chpos, previtem[MAXVAL];
struct _parse_rule *rules;
if(!configfile) return;
f = fopen(configfile, "r");
if(!f) return;
rules = &config_parse_rules[0];
memset(previtem, 0, MAXVAL);
while(fgets(&line[0], MAXVAL-1, f)){
if(line[0] == ';' || line[0] == '#') continue;
chpos = strchr(line, '=');
if(chpos){
trimBuffer(chpos+1);
*chpos = '\0';
int i = 0;
while(rules[i].name){
if(strcmp(line, rules[i].name) == 0) {
if(strcmp(line, previtem)) print_config_item(cfg, rules, i);
snprintf(previtem, MAXVAL-1, "%s", line);
break;
}
i++;
}
if(!rules[i].name) printf("unknown key: \"%s\" \n", line);
}
}
fclose(f);
}

66
src/cfg.h Normal file
View File

@ -0,0 +1,66 @@
/*
* cfg.h, SJ
*/
#ifndef _CFG_H
#define _CFG_H
#include "config.h"
struct __config {
char username[MAXVAL];
char hostid[MAXVAL];
char pidfile[MAXVAL];
char listen_addr[MAXVAL];
int listen_port;
char clamd_addr[MAXVAL];
int clamd_port;
char clamd_socket[MAXVAL];
int use_antivirus;
char memcached_servers[MAXVAL];
int memcached_ttl;
int number_of_worker_processes;
int max_requests_per_child;
int max_connections;
int backlog;
char workdir[MAXVAL];
char queuedir[MAXVAL];
char deferdir[MAXVAL];
int verbosity;
char locale[MAXVAL];
int session_timeout;
char piler_header_field[MAXVAL];
unsigned char key[KEYLEN];
unsigned char iv[MAXVAL];
// mysql stuff
char mysqlhost[MAXVAL];
int mysqlport;
char mysqlsocket[MAXVAL];
char mysqluser[MAXVAL];
char mysqlpwd[MAXVAL];
char mysqldb[MAXVAL];
int mysql_connect_timeout;
// sqlite3 stuff
char sqlite3[MAXVAL];
char sqlite3_pragma[MAXVAL];
};
#endif /* _CFG_H */

135
src/clamd.c Normal file
View File

@ -0,0 +1,135 @@
/*
* clamd.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <piler.h>
int clamd_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg){
int s, n;
char *p, *q, buf[MAXBUFSIZE], scan_cmd[SMALLBUFSIZE];
struct sockaddr_un server;
memset(avinfo, 0, SMALLBUFSIZE);
chmod(tmpfile, 0644);
strcpy(server.sun_path, cfg->clamd_socket);
server.sun_family = AF_UNIX;
if((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1){
syslog(LOG_PRIORITY, "ERR: create socket");
return AV_ERROR;
}
if(connect(s, (struct sockaddr *)&server, strlen(server.sun_path) + sizeof (server.sun_family)) == -1){
syslog(LOG_PRIORITY, "CLAMD ERR: connect to %s", cfg->clamd_socket);
return AV_ERROR;
}
/* issue the SCAN command with full path to the temporary directory */
memset(scan_cmd, 0, SMALLBUFSIZE);
snprintf(scan_cmd, SMALLBUFSIZE-1, "SCAN %s/%s\r\n", cfg->workdir, tmpfile);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD CMD: %s", tmpfile, scan_cmd);
send(s, scan_cmd, strlen(scan_cmd), 0);
/* read CLAMD's answers */
n = recvtimeout(s, buf, MAXBUFSIZE, TIMEOUT);
close(s);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD DEBUG: %d %s", tmpfile, n, buf);
if(strcasestr(buf, CLAMD_RESP_INFECTED)){
p = strchr(buf, ' ');
if(p){
q = strrchr(p, ' ');
if(q){
*q = '\0';
p++;
strncpy(avinfo, p, SMALLBUFSIZE-1);
}
}
return AV_VIRUS;
}
return AV_OK;
}
int clamd_net_scan(char *tmpfile, char *engine, char *avinfo, struct __config *cfg){
int n, psd;
char *p, *q, buf[MAXBUFSIZE], scan_cmd[SMALLBUFSIZE];
struct in_addr addr;
struct sockaddr_in clamd_addr;
memset(avinfo, 0, SMALLBUFSIZE);
chmod(tmpfile, 0644);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: trying to pass to clamd", tmpfile);
if((psd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
syslog(LOG_PRIORITY, "%s: ERR: create socket", tmpfile);
return AV_ERROR;
}
clamd_addr.sin_family = AF_INET;
clamd_addr.sin_port = htons(cfg->clamd_port);
inet_aton(cfg->clamd_addr, &addr);
clamd_addr.sin_addr.s_addr = addr.s_addr;
bzero(&(clamd_addr.sin_zero), 8);
if(connect(psd, (struct sockaddr *)&clamd_addr, sizeof(struct sockaddr)) == -1){
syslog(LOG_PRIORITY, "%s: CLAMD ERR: connect to %s %d", tmpfile, cfg->clamd_addr, cfg->clamd_port);
return AV_ERROR;
}
memset(scan_cmd, 0, SMALLBUFSIZE);
snprintf(scan_cmd, SMALLBUFSIZE-1, "SCAN %s/%s\r\n", cfg->workdir, tmpfile);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD CMD: %s", tmpfile, scan_cmd);
send(psd, scan_cmd, strlen(scan_cmd), 0);
n = recvtimeout(psd, buf, MAXBUFSIZE, TIMEOUT);
close(psd);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: CLAMD DEBUG: %d %s", tmpfile, n, buf);
if(strcasestr(buf, CLAMD_RESP_INFECTED)){
p = strchr(buf, ' ');
if(p){
q = strrchr(p, ' ');
if(q){
*q = '\0';
p++;
strncpy(avinfo, p, SMALLBUFSIZE-1);
}
}
return AV_VIRUS;
}
return AV_OK;
}

107
src/config.h Normal file
View File

@ -0,0 +1,107 @@
/*
* config.h, SJ
*/
#ifndef _CONFIG_H
#define _CONFIG_H
#include <syslog.h>
#include "piler-config.h"
#include "params.h"
#define PROGNAME "piler"
#define VERSION "0.1.2"
#define PROGINFO VERSION ", Janos SUTO <sj@acts.hu>\n\n" CONFIGURE_PARAMS "\n\nSend bugs/issues to https://jira.acts.hu:8443/\n"
#define HOSTID "mailarchiver"
#define CONFIG_FILE CONFDIR "/piler.conf"
#define WORK_DIR DATADIR "/spool/piler/tmp"
#define QUEUE_DIR DATADIR "/spool/piler/new"
#define DEFER_DIR DATADIR "/spool/piler/deferred"
#define CLAMD_SOCKET "/tmp/clamd"
#define PIDFILE "/var/run/piler/piler.pid"
#define QUARANTINELEN 255
#define TIMEOUT 60
#define TIMEOUT_USEC 500000
#define SESSION_TIMEOUT 420
#define MAXBUFSIZE 8192
#define SMALLBUFSIZE 512
#define BIGBUFSIZE 65535
#define MAXVAL 256
#define RANDOM_POOL "/dev/urandom"
#define RND_STR_LEN 36
#define BUFLEN 32
#define IPLEN 16+1
#define KEYLEN 56
#define CRLF "\n"
#define MEMCACHED_CLAPF_PREFIX "_piler"
#define MAX_MEMCACHED_KEY_LEN 250
#define MEMCACHED_SUCCESS 0
#define MEMCACHED_FAILURE 1
#define MEMCACHED_COUNTERS_LAST_UPDATE MEMCACHED_CLAPF_PREFIX ":counters_last_update"
#define MEMCACHED_MSGS_RCVD MEMCACHED_CLAPF_PREFIX ":rcvd"
#define MEMCACHED_MSGS_VIRUS MEMCACHED_CLAPF_PREFIX ":virus"
#define MEMCACHED_MSGS_DUPLICATE MEMCACHED_CLAPF_PREFIX ":duplicate"
#define LOG_PRIORITY LOG_INFO
#define _LOG_INFO 3
#define _LOG_DEBUG 5
#define MAX_RCPT_TO 128
#ifdef HAVE_SQLITE3
#define MAX_KEY_VAL 9223372036854775807ULL
#else
#define MAX_KEY_VAL 18446744073709551615ULL
#endif
#define MIN_WORD_LEN 3
#define MAX_WORD_LEN 25
#define MAX_TOKEN_LEN 4*MAX_WORD_LEN
#define URL_LEN 48
#define DELIMITER ' '
#define SPAMINESS_HEADER_FIELD "X-Clapf-spamicity: "
#define BOUNDARY_LEN 255
#define JUNK_REPLACEMENT_CHAR 'j'
#define MAX_ATTACHMENTS 8
/* SQL stuff */
#define SQL_SPHINX_TABLE "sph_index"
#define SQL_METADATA_TABLE "metadata"
#define SQL_HEADER_TABLE "header"
#define SQL_BODY_TABLE "body"
#define SQL_COUNTER_TABLE "counter"
/* TRE stuff */
#define NUM_OF_REGEXES 20
/* Error codes */
#define OK 0
#define ERR 1
#define ERR_EXISTS 2
#define AVIR_OK 0
#define AVIR_VIRUS 1
#endif /* _CONFIG_H */

121
src/counters.c Normal file
View File

@ -0,0 +1,121 @@
/*
* counters.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <syslog.h>
#include <piler.h>
struct __counters loadCounters(struct session_data *sdata, struct __config *cfg){
char buf[SMALLBUFSIZE];
struct __counters counters;
bzero(&counters, sizeof(counters));
snprintf(buf, SMALLBUFSIZE-1, "SELECT rcvd, virus, duplicate FROM %s", SQL_COUNTER_TABLE);
#ifdef NEED_MYSQL
MYSQL_RES *res;
MYSQL_ROW row;
if(mysql_real_query(&(sdata->mysql), buf, strlen(buf)) == 0){
res = mysql_store_result(&(sdata->mysql));
if(res != NULL){
row = mysql_fetch_row(res);
if(row){
counters.c_rcvd = strtoull(row[0], NULL, 10);
counters.c_virus = strtoull(row[1], NULL, 10);
counters.c_duplicate = strtoull(row[2], NULL, 10);
}
mysql_free_result(res);
}
}
#endif
return counters;
}
void updateCounters(struct session_data *sdata, struct __data *data, struct __counters *counters, struct __config *cfg){
char buf[MAXBUFSIZE];
#ifdef HAVE_MEMCACHED
unsigned long long mc, rcvd;
struct __counters c;
char key[MAX_MEMCACHED_KEY_LEN];
unsigned int flags=0;
if(cfg->update_counters_to_memcached == 1){
/* increment counters to memcached */
if(memcached_increment(&(data->memc), MEMCACHED_MSGS_RCVD, strlen(MEMCACHED_MSGS_RCVD), counters->c_rcvd, &mc) == MEMCACHED_SUCCESS){
rcvd = mc;
if(counters->c_ham > 0) memcached_increment(&(data->memc), MEMCACHED_MSGS_HAM, strlen(MEMCACHED_MSGS_HAM), counters->c_ham, &mc);
if(counters->c_virus > 0) memcached_increment(&(data->memc), MEMCACHED_MSGS_VIRUS, strlen(MEMCACHED_MSGS_VIRUS), counters->c_virus, &mc);
if(counters->c_duplicate > 0) memcached_increment(&(data->memc), MEMCACHED_MSGS_DUPLICATE, strlen(MEMCACHED_MSGS_DUPLICATE), counters->c_duplicate, &mc);
bzero(&c, sizeof(c));
snprintf(buf, MAXBUFSIZE-1, "%s %s %s %s", MEMCACHED_MSGS_RCVD, MEMCACHED_MSGS_VIRUS, MEMCACHED_MSGS_DUPLICATE, MEMCACHED_COUNTERS_LAST_UPDATE);
if(memcached_mget(&(data->memc), buf) == MEMCACHED_SUCCESS){
while((memcached_fetch_result(&(data->memc), &key[0], &buf[0], &flags))){
if(!strcmp(key, MEMCACHED_MSGS_RCVD)) c.c_rcvd = strtoull(buf, NULL, 10);
else if(!strcmp(key, MEMCACHED_MSGS_VIRUS)) c.c_virus = strtoull(buf, NULL, 10);
else if(!strcmp(key, MEMCACHED_MSGS_DUPLICATE)) c.c_duplicate = strtoull(buf, NULL, 10);
else if(!strcmp(key, MEMCACHED_COUNTERS_LAST_UPDATE)) mc = strtoull(buf, NULL, 10);
}
if(sdata->now - mc > cfg->memcached_to_db_interval && c.c_rcvd > 0 && c.c_rcvd >= rcvd){
snprintf(buf, SMALLBUFSIZE-1, "%ld", sdata->now); memcached_set(&(data->memc), MEMCACHED_COUNTERS_LAST_UPDATE, strlen(MEMCACHED_COUNTERS_LAST_UPDATE), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "UPDATE `%s` SET rcvd=%llu, virus=%llu, duplicate=%llu", c.c_rcvd, c.c_virus, c.c_duplicate);
//if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: update counters: %s", sdata->ttmpfile, buf);
goto EXEC_SQL;
}
}
}
else {
c = loadCounters(sdata, cfg);
snprintf(buf, SMALLBUFSIZE-1, "%ld", sdata->now); memcached_add(&(data->memc), MEMCACHED_COUNTERS_LAST_UPDATE, strlen(MEMCACHED_COUNTERS_LAST_UPDATE), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "%llu", c.c_virus + counters->c_virus); memcached_add(&(data->memc), MEMCACHED_MSGS_VIRUS, strlen(MEMCACHED_MSGS_VIRUS), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "%llu", c.c_rcvd + counters->c_rcvd); memcached_add(&(data->memc), MEMCACHED_MSGS_RCVD, strlen(MEMCACHED_MSGS_RCVD), buf, strlen(buf), 0, 0);
snprintf(buf, SMALLBUFSIZE-1, "%llu", c.c_duplicate + counters->c_duplicate); memcached_add(&(data->memc), MEMCACHED_MSGS_DUPLICATE, strlen(MEMCACHED_MSGS_DUPLICATE), buf, strlen(buf), 0, 0);
}
}
else {
#endif
snprintf(buf, SMALLBUFSIZE-1, "UPDATE `%s` SET rcvd=rcvd+%llu, virus=virus+%llu, duplicate=duplicate+%llu", SQL_COUNTER_TABLE, counters->c_rcvd, counters->c_virus, counters->c_duplicate);
//if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: update counters: %s", sdata->ttmpfile, buf);
#ifdef HAVE_MEMCACHED
EXEC_SQL:
#endif
#ifdef NEED_MYSQL
mysql_real_query(&(sdata->mysql), buf, strlen(buf));
#endif
#ifdef HAVE_MEMCACHED
}
#endif
}

282
src/decoder.c Normal file
View File

@ -0,0 +1,282 @@
/*
* decoder.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "decoder.h"
#include "htmlentities.h"
#include "config.h"
static int b64[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};
static char hex_table[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static int compmi(const void *m1, const void *m2){
struct mi *mi1 = (struct mi *) m1;
struct mi *mi2 = (struct mi *) m2;
return strcmp(mi1->entity, mi2->entity);
}
void sanitiseBase64(char *s){
char *p1;
if(s == NULL) return;
for(; *s; s++){
if(b64[(unsigned int)(*s & 0xFF)] == 255){
for(p1 = s; p1[0] != '\0'; p1++)
p1[0] = p1[1];
}
}
}
int decodeBase64(char *p){
int i, j, n[4], k1, k2, len=0;
char s[5], s2[3], puf[MAXBUFSIZE];
if(strlen(p) < 4 || strlen(p) > MAXBUFSIZE/2)
return 0;
for(i=0; i<strlen(p); i++){
memcpy(s, p+i, 4);
s[4] = '\0';
i += 3;
if(strlen(s) == 4){
memset(s2, 0, 3);
for(j=0; j<4; j++){
k1 = s[j];
n[j] = b64[k1];
}
k1 = n[0]; k1 = k1 << 2;
k2 = n[1]; k2 = k2 >> 4;
s2[0] = k1 | k2;
k1 = (n[1] & 0x0F) << 4;
k2 = n[2]; k2 = k2 >> 2;
s2[1] = k1 | k2;
k1 = n[2] << 6;
k2 = n[3] >> 0;
s2[2] = k1 | k2;
// this is binary safe
memcpy(puf+len, s2, 3);
len += 3;
}
}
*(puf+len) = '\0';
snprintf(p, MAXBUFSIZE-1, "%s", puf);
return len;
}
void decodeUTF8(char *p){
int i, k=0, a, b;
unsigned char c, c1, c2;
if(p == NULL) return;
for(i=0; i<strlen(p); i++){
c = p[i];
if(p[i] == '=' && isxdigit(p[i+1]) && isxdigit(p[i+2]) &&
p[i+3] == '=' && isxdigit(p[i+4]) && isxdigit(p[i+5])){
a = p[i+1];
b = p[i+2];
c1 = 16 * hex_table[a] + hex_table[b];
a = p[i+4];
b = p[i+5];
c2 = 16 * hex_table[a] + hex_table[b];
if(c1 >= 192 && c1 <= 223){
c = 64 * (c1 - 192) + c2 - 128;
i += 5;
}
}
if(c >= 192 && c <= 223){
c = 64 * (c - 192) + p[i+1] - 128;
i++;
}
p[k] = c;
k++;
}
p[k] = '\0';
}
void decodeQP(char *p){
int i, k=0, a, b;
char c;
if(p == NULL) return;
for(i=0; i<strlen((char*)p); i++){
c = p[i];
if(p[i] == '=' && isxdigit(p[i+1]) && isxdigit(p[i+2])){
a = p[i+1];
b = p[i+2];
c = 16 * hex_table[a] + hex_table[b];
i += 2;
}
p[k] = c;
k++;
}
p[k] = '\0';
}
void decodeHTML(char *s){
char *p;
int i, c, k=0, unknown='q';
struct mi key, *res;
if(s == NULL) return;
for(i=0; i<strlen(s); i++){
c = s[i];
if(*(s+i) == '&'){
p = strchr(s+i, ';');
if(p){
*p = '\0';
if(*(s+i+1) == '#'){
c = atoi(s+i+2);
if(c == 0) c = unknown;
}
else {
key.entity = s+i;
res = bsearch(&key, htmlentities, NUM_OF_HTML_ENTITIES, sizeof(struct mi), compmi);
if(res && res->val <= 255) c = res->val;
else c = unknown;
}
i += strlen(s+i);
*p = ';';
}
}
s[k] = c;
k++;
}
s[k] = '\0';
}
void decodeURL(char *p){
int i, c, k=0, a, b;
if(p == NULL) return;
for(i=0; i<strlen(p); i++){
switch(p[i]){
case '+':
c = ' ';
break;
case '%':
if(isxdigit(p[i+1]) && isxdigit(p[i+2])){
a = p[i+1];
b = p[i+2];
c = 16 * hex_table[a] + hex_table[b];
i += 2;
}
else
c = p[i];
break;
default:
c = p[i];
break;
}
p[k] = c;
k++;
}
p[k] = '\0';
}

15
src/decoder.h Normal file
View File

@ -0,0 +1,15 @@
/*
* decoder.h, SJ
*/
#ifndef _DECODER_H
#define _DECODER_H
void sanitiseBase64(char *s);
int decodeBase64(char *p);
void decodeUTF8(char *p);
void decodeQP(char *p);
void decodeHTML(char *p);
void decodeURL(char *p);
#endif /* _DECODER_H */

205
src/defs.h Normal file
View File

@ -0,0 +1,205 @@
/*
* defs.h, SJ
*/
#ifndef _DEFS_H
#define _DEFS_H
#ifdef NEED_MYSQL
#include <mysql.h>
#endif
#ifdef NEED_SQLITE3
#include <sqlite3.h>
/* for older versions of sqlite3 do not have the sqlite3_prepare_v2() function, 2009.12.30, SJ */
#if SQLITE_VERSION_NUMBER < 3006000
#define sqlite3_prepare_v2 sqlite3_prepare
#endif
#endif
#ifdef HAVE_TRE
#include <tre/tre.h>
#include <tre/regex.h>
#endif
#include <openssl/sha.h>
#include "config.h"
#define MSG_UNDEF -1
#define MSG_BODY 0
#define MSG_RECEIVED 1
#define MSG_FROM 2
#define MSG_TO 3
#define MSG_CC 4
#define MSG_SUBJECT 5
#define MSG_CONTENT_TYPE 6
#define MSG_CONTENT_TRANSFER_ENCODING 7
#define MSG_CONTENT_DISPOSITION 8
#define MSG_MESSAGE_ID 9
#define MAXHASH 8171
#define BASE64_RATIO 1.33333333
#define DIGEST_LENGTH SHA256_DIGEST_LENGTH
#define UNDEF 0
#define READY 1
#define BUSY 2
#define PROCESSED 3
struct attachment {
int size;
char type[SMALLBUFSIZE];
char filename[SMALLBUFSIZE];
};
struct url {
char url_str[URL_LEN];
struct url *r;
};
struct boundary {
char boundary_str[BOUNDARY_LEN];
struct boundary *r;
};
struct _state {
int message_state;
int is_header;
int textplain;
int texthtml;
int octetstream;
int message_rfc822;
int base64;
int has_base64;
int utf8;
int qp;
int htmltag;
int style;
int skip_html;
int ipcnt;
int has_to_dump;
int fd;
int num_of_msword;
int num_of_images;
int realbinary;
int content_type_is_set;
int train_mode;
unsigned long c_shit;
unsigned long l_shit;
unsigned long line_num;
char ip[SMALLBUFSIZE];
char hostname[SMALLBUFSIZE];
char miscbuf[MAX_TOKEN_LEN];
char qpbuf[MAX_TOKEN_LEN];
char attachedfile[RND_STR_LEN+SMALLBUFSIZE];
char from[SMALLBUFSIZE];
char message_id[SMALLBUFSIZE];
unsigned long n_token;
unsigned long n_subject_token;
unsigned long n_body_token;
unsigned long n_chain_token;
struct url *urls;
int found_our_signo;
struct boundary *boundaries;
int n_attachments;
struct attachment attachments[MAX_ATTACHMENTS];
char b_from[SMALLBUFSIZE], b_to[SMALLBUFSIZE], b_subject[MAXBUFSIZE], b_body[BIGBUFSIZE];
};
struct session_data {
char ttmpfile[SMALLBUFSIZE], tre;
char mailfrom[SMALLBUFSIZE], rcptto[MAX_RCPT_TO][SMALLBUFSIZE], client_addr[SMALLBUFSIZE];
char acceptbuf[SMALLBUFSIZE];
char whitelist[MAXBUFSIZE], blacklist[MAXBUFSIZE];
int fd, hdr_len, tot_len, num_of_rcpt_to, rav;
int need_scan;
float __acquire, __parsed, __av, __store, __compress, __encrypt;
SHA256_CTX context;
unsigned char md[DIGEST_LENGTH];
char bodydigest[2*DIGEST_LENGTH+1];
time_t now, sent;
#ifdef NEED_MYSQL
MYSQL mysql;
#endif
#ifdef NEED_SQLITE3
sqlite3 *db;
#endif
};
#ifdef HAVE_MEMCACHED
#include <stdbool.h>
#include <netinet/in.h>
struct flags {
bool no_block:1;
bool no_reply:1;
bool tcp_nodelay:1;
bool tcp_keepalive:1;
};
struct memcached_server {
struct flags flags;
int fd;
unsigned int snd_timeout;
unsigned int rcv_timeout;
int send_size;
int recv_size;
unsigned int tcp_keepidle;
int last_read_bytes;
char *result;
char buf[MAXBUFSIZE];
struct sockaddr_in addr;
char server_ip[16];
int server_port;
char initialised;
};
#endif
struct __data {
struct url *blackhole;
#ifdef HAVE_LIBCLAMAV
struct cl_engine *engine;
#endif
#ifdef HAVE_TRE
regex_t pregs[NUM_OF_REGEXES];
int n_regex;
#endif
#ifdef HAVE_MEMCACHED
struct memcached_server memc;
#endif
};
struct __counters {
unsigned long long c_rcvd;
unsigned long long c_virus;
unsigned long long c_duplicate;
};
#endif /* _DEFS_H */

60
src/digest.c Normal file
View File

@ -0,0 +1,60 @@
/*
* digest.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <piler.h>
#include <openssl/evp.h>
int make_body_digest(struct session_data *sdata){
int i=0, n, fd;
char *p, *body=NULL;
unsigned char buf[MAXBUFSIZE];
memset(sdata->bodydigest, 0, 2*DIGEST_LENGTH+1);
SHA256_Init(&(sdata->context));
fd = open(sdata->ttmpfile, O_RDONLY);
if(fd == -1) return -1;
while((n = read(fd, buf, MAXBUFSIZE)) > 0){
body = (char *)&buf[0];
i++;
if(i == 1){
p = strstr((char*)buf, "\n\n");
if(p){
body = p+2;
n = strlen(body);
} else {
p = strstr((char*)buf, "\n\r\n");
if(p){
body = p+3;
n = strlen(body);
}
}
}
SHA256_Update(&(sdata->context), body, n);
}
close(fd);
SHA256_Final(sdata->md, &(sdata->context));
for(i=0;i<DIGEST_LENGTH;i++)
snprintf(sdata->bodydigest + i*2, 2*DIGEST_LENGTH, "%02x", sdata->md[i]);
return 0;
}

81
src/dirs.c Normal file
View File

@ -0,0 +1,81 @@
/*
* dirs.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include <piler.h>
void createdir(char *path, uid_t uid, gid_t gid, mode_t mode);
void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid){
char *p;
p = strrchr(cfg->workdir, '/');
if(p){
*p = '\0';
createdir(cfg->workdir, uid, gid, 0755);
*p = '/';
}
createdir(cfg->workdir, uid, gid, 0711);
p = strrchr(cfg->queuedir, '/');
if(p){
*p = '\0';
createdir(cfg->queuedir, uid, gid, 0755);
*p = '/';
}
createdir(cfg->queuedir, uid, gid, 0700);
p = strrchr(cfg->deferdir, '/');
if(p){
*p = '\0';
createdir(cfg->deferdir, uid, gid, 0755);
*p = '/';
}
createdir(cfg->deferdir, uid, gid, 0700);
p = strrchr(cfg->sqlite3, '/');
if(p){
*p = '\0';
createdir(cfg->sqlite3, uid, gid, 0755);
*p = '/';
}
p = strrchr(cfg->pidfile, '/');
if(p){
*p = '\0';
createdir(cfg->pidfile, uid, gid, 0755);
*p = '/';
}
}
void createdir(char *path, uid_t uid, gid_t gid, mode_t mode){
struct stat st;
int rc;
if(strlen(path) > 2){
if(path[strlen(path)-1] == '/') path[strlen(path)-1] = '\0';
if(stat(path, &st)){
if(mkdir(path, mode) == 0){
rc = chown(path, uid, gid);
syslog(LOG_PRIORITY, "created directory: *%s*", path);
}
else syslog(LOG_PRIORITY, "could not create directory: *%s*", path);
}
}
}

31
src/errmsg.h Normal file
View File

@ -0,0 +1,31 @@
/*
* errmsg.h, SJ
*/
#ifndef _ERRMSG_H
#define _ERRMSG_H
#define ERR_CANNOT_READ_FROM_POOL "ERR: cannot read from pool"
#define ERR_SIGACTION "sigaction failed"
#define ERR_OPEN_SOCKET "ERR: cannot open socket"
#define ERR_SET_SOCK_OPT "ERR: cannot set socket option"
#define ERR_BIND_TO_PORT "ERR: cannot bind to port"
#define ERR_LISTEN "ERR: cannot listen"
#define ERR_SETUID "ERR: setuid()"
#define ERR_SETGID "ERR: setgid()"
#define ERR_SELECT "ERR: select()"
#define ERR_CHDIR "ERR: chdir() to working directory failed"
#define ERR_OPEN_TMP_FILE "ERR: opening a tempfile"
#define ERR_TIMED_OUT "ERR: timed out"
#define ERR_FORK_FAILED "ERR: cannot fork()"
#define ERR_MYSQL_CONNECT "Cannot connect to mysql server"
#define ERR_PSQL_CONNECT "Cannot connect to PSql server"
#define ERR_SQLITE3_OPEN "Cannot open sqlite3 database"
#define ERR_SQL_DATA "No valid data from sql table"
#define ERR_NON_EXISTENT_USER "ERR: non existent user in config file, see the 'username' variable"
#define ERR_READING_KEY "ERR: reading cipher key"
#endif /* _ERRMSG_H */

38
src/html.h Normal file
View File

@ -0,0 +1,38 @@
struct html_tag {
unsigned char length;
char *entity;
};
#define NUM_OF_SKIP_TAGS2 10
struct html_tag skip_html_tags2[] = {
{ 4, "html" },
{ 5, "/html" },
{ 5, "/body" },
{ 4, "meta" },
{ 4, "head" },
{ 5, "/head" },
{ 5, "style" },
{ 6, "/style" },
{ 3, "div" },
{ 4, "/div" }
};
#define NUM_OF_SKIP_TAGS 11
struct html_tag skip_html_tags[] = {
{ 5, "style" },
{ 4, "dir=" },
{ 8, "content=" },
{ 5, "name=" },
{ 3, "id=" },
{ 2, "v:" },
{ 6, "class=" },
{ 5, "xmlns" },
{ 10, "http-equiv" },
{ 7, "spidmax" },
{ 5, "data=" }
};

288
src/htmlentities.h Normal file
View File

@ -0,0 +1,288 @@
/*
* htmlentities.h, SJ
*
* taken from dspam's src/decoder.c, then qsort()'ed:
qsort(htmlentities, NUM_OF_HTML_ENTITIES, sizeof(struct mi), compmi);
for(i=0; i<NUM_OF_HTML_ENTITIES; i++){
printf(" { %d, \"%s\" },\n", htmlentities[i].val, htmlentities[i].entity);
}
*/
#ifndef _HTMLENTITIES_H
#define _HTMLENTITIES_H
#define NUM_OF_HTML_ENTITIES 258
struct mi {
unsigned int val;
char *entity;
};
struct mi htmlentities[] = {
{ 198, "&AElig" },
{ 193, "&Aacute" },
{ 194, "&Acirc" },
{ 192, "&Agrave" },
{ 913, "&Alpha" },
{ 197, "&Aring" },
{ 195, "&Atilde" },
{ 196, "&Auml" },
{ 914, "&Beta" },
{ 199, "&Ccedil" },
{ 935, "&Chi" },
{ 8225, "&Dagger" },
{ 916, "&Delta" },
{ 208, "&ETH" },
{ 201, "&Eacute" },
{ 202, "&Ecirc" },
{ 200, "&Egrave" },
{ 917, "&Epsilon" },
{ 919, "&Eta" },
{ 203, "&Euml" },
{ 915, "&Gamma" },
{ 205, "&Iacute" },
{ 206, "&Icirc" },
{ 204, "&Igrave" },
{ 921, "&Iota" },
{ 207, "&Iuml" },
{ 922, "&Kappa" },
{ 923, "&Lambda" },
{ 924, "&Mu" },
{ 209, "&Ntilde" },
{ 925, "&Nu" },
{ 338, "&OElig" },
{ 211, "&Oacute" },
{ 212, "&Ocirc" },
{ 210, "&Ograve" },
{ 937, "&Omega" },
{ 927, "&Omicron" },
{ 216, "&Oslash" },
{ 213, "&Otilde" },
{ 214, "&Ouml" },
{ 934, "&Phi" },
{ 928, "&Pi" },
{ 8243, "&Prime" },
{ 936, "&Psi" },
{ 929, "&Rho" },
{ 352, "&Scaron" },
{ 931, "&Sigma" },
{ 222, "&THORN" },
{ 932, "&Tau" },
{ 920, "&Theta" },
{ 218, "&Uacute" },
{ 219, "&Ucirc" },
{ 217, "&Ugrave" },
{ 933, "&Upsilon" },
{ 220, "&Uuml" },
{ 926, "&Xi" },
{ 221, "&Yacute" },
{ 376, "&Yuml" },
{ 918, "&Zeta" },
{ 225, "&aacute" },
{ 226, "&acirc" },
{ 180, "&acute" },
{ 230, "&aelig" },
{ 224, "&agrave" },
{ 8501, "&alefsym" },
{ 945, "&alpha" },
{ 38, "&amp" },
{ 38, "&amp" },
{ 8743, "&and" },
{ 8736, "&ang" },
{ 39, "&apos" },
{ 229, "&aring" },
{ 8776, "&asymp" },
{ 227, "&atilde" },
{ 228, "&auml" },
{ 8222, "&bdquo" },
{ 946, "&beta" },
{ 166, "&brvbar" },
{ 8226, "&bull" },
{ 8745, "&cap" },
{ 231, "&ccedil" },
{ 184, "&cedil" },
{ 162, "&cent" },
{ 967, "&chi" },
{ 710, "&circ" },
{ 9827, "&clubs" },
{ 8773, "&cong" },
{ 169, "&copy" },
{ 8629, "&crarr" },
{ 8746, "&cup" },
{ 164, "&curren" },
{ 8659, "&dArr" },
{ 8224, "&dagger" },
{ 8595, "&darr" },
{ 176, "&deg" },
{ 948, "&delta" },
{ 9830, "&diams" },
{ 247, "&divide" },
{ 233, "&eacute" },
{ 234, "&ecirc" },
{ 232, "&egrave" },
{ 8709, "&empty" },
{ 8195, "&emsp" },
{ 8194, "&ensp" },
{ 949, "&epsilon" },
{ 8801, "&equiv" },
{ 951, "&eta" },
{ 240, "&eth" },
{ 235, "&euml" },
{ 8364, "&euro" },
{ 8707, "&exist" },
{ 402, "&fnof" },
{ 8704, "&forall" },
{ 189, "&frac12" },
{ 188, "&frac14" },
{ 190, "&frac34" },
{ 8260, "&frasl" },
{ 947, "&gamma" },
{ 8805, "&ge" },
{ 62, "&gt" },
{ 62, "&gt" },
{ 8660, "&hArr" },
{ 8596, "&harr" },
{ 9829, "&hearts" },
{ 8230, "&hellip" },
{ 237, "&iacute" },
{ 238, "&icirc" },
{ 161, "&iexcl" },
{ 236, "&igrave" },
{ 8465, "&image" },
{ 8734, "&infin" },
{ 8747, "&int" },
{ 953, "&iota" },
{ 191, "&iquest" },
{ 8712, "&isin" },
{ 239, "&iuml" },
{ 954, "&kappa" },
{ 8656, "&lArr" },
{ 955, "&lambda" },
{ 9001, "&lang" },
{ 171, "&laquo" },
{ 8592, "&larr" },
{ 8968, "&lceil" },
{ 8220, "&ldquo" },
{ 8804, "&le" },
{ 8970, "&lfloor" },
{ 8727, "&lowast" },
{ 9674, "&loz" },
{ 8206, "&lrm" },
{ 8249, "&lsaquo" },
{ 8216, "&lsquo" },
{ 60, "&lt" },
{ 60, "&lt" },
{ 175, "&macr" },
{ 8212, "&mdash" },
{ 181, "&micro" },
{ 183, "&middot" },
{ 8722, "&minus" },
{ 956, "&mu" },
{ 8711, "&nabla" },
{ 32, "&nbsp" },
{ 160, "&nbsp" },
{ 8211, "&ndash" },
{ 8800, "&ne" },
{ 8715, "&ni" },
{ 172, "&not" },
{ 8713, "&notin" },
{ 8836, "&nsub" },
{ 241, "&ntilde" },
{ 957, "&nu" },
{ 243, "&oacute" },
{ 244, "&ocirc" },
{ 339, "&oelig" },
{ 242, "&ograve" },
{ 8254, "&oline" },
{ 969, "&omega" },
{ 959, "&omicron" },
{ 8853, "&oplus" },
{ 8744, "&or" },
{ 170, "&ordf" },
{ 186, "&ordm" },
{ 248, "&oslash" },
{ 245, "&otilde" },
{ 8855, "&otimes" },
{ 246, "&ouml" },
{ 182, "&para" },
{ 8706, "&part" },
{ 8240, "&permil" },
{ 8869, "&perp" },
{ 966, "&phi" },
{ 960, "&pi" },
{ 982, "&piv" },
{ 177, "&plusmn" },
{ 163, "&pound" },
{ 8242, "&prime" },
{ 8719, "&prod" },
{ 8733, "&prop" },
{ 968, "&psi" },
{ 34, "&quot" },
{ 34, "&quot" },
{ 8658, "&rArr" },
{ 8730, "&radic" },
{ 9002, "&rang" },
{ 187, "&raquo" },
{ 8594, "&rarr" },
{ 8969, "&rceil" },
{ 8221, "&rdquo" },
{ 8476, "&real" },
{ 174, "&reg" },
{ 8971, "&rfloor" },
{ 961, "&rho" },
{ 8207, "&rlm" },
{ 8250, "&rsaquo" },
{ 8217, "&rsquo" },
{ 8218, "&sbquo" },
{ 353, "&scaron" },
{ 8901, "&sdot" },
{ 167, "&sect" },
{ 173, "&shy" },
{ 963, "&sigma" },
{ 962, "&sigmaf" },
{ 8764, "&sim" },
{ 9824, "&spades" },
{ 8834, "&sub" },
{ 8838, "&sube" },
{ 8721, "&sum" },
{ 8835, "&sup" },
{ 185, "&sup1" },
{ 178, "&sup2" },
{ 179, "&sup3" },
{ 8839, "&supe" },
{ 223, "&szlig" },
{ 964, "&tau" },
{ 8756, "&there4" },
{ 952, "&theta" },
{ 977, "&thetasym" },
{ 8201, "&thinsp" },
{ 254, "&thorn" },
{ 732, "&tilde" },
{ 215, "&times" },
{ 8482, "&trade" },
{ 8657, "&uArr" },
{ 250, "&uacute" },
{ 8593, "&uarr" },
{ 251, "&ucirc" },
{ 249, "&ugrave" },
{ 168, "&uml" },
{ 978, "&upsih" },
{ 965, "&upsilon" },
{ 252, "&uuml" },
{ 8472, "&weierp" },
{ 958, "&xi" },
{ 253, "&yacute" },
{ 165, "&yen" },
{ 255, "&yuml" },
{ 950, "&zeta" },
{ 8205, "&zwj" },
{ 8204, "&zwnj" }
};
#endif /* _HTMLENTITIES_H */

27
src/ijc.h Normal file
View File

@ -0,0 +1,27 @@
/*
* ijc.h, SJ
*/
static char invalid_junk_characters[] = {
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ','', ' ','',' ',' ',
'x',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ','<EFBFBD>','<EFBFBD>',' ', '<EFBFBD>','<EFBFBD>','<EFBFBD>',' ', '<EFBFBD>',' ','<EFBFBD>','<EFBFBD>', '<EFBFBD>','<EFBFBD>','<EFBFBD>','<EFBFBD>',
'<EFBFBD>','<EFBFBD>','<EFBFBD>','<EFBFBD>', ' ','<EFBFBD>','<EFBFBD>',' ', '<EFBFBD>','<EFBFBD>','<EFBFBD>','<EFBFBD>', ' ','<EFBFBD>','<EFBFBD>','<EFBFBD>',
' ',' ','<EFBFBD>','<EFBFBD>', '<EFBFBD>','<EFBFBD>','<EFBFBD>','<EFBFBD>', '<EFBFBD>',' ',' ','<EFBFBD>', '<EFBFBD>',' ',' ','<EFBFBD>',
'<EFBFBD>','<EFBFBD>','<EFBFBD>',' ', '<EFBFBD>',' ',' ','<EFBFBD>', '<EFBFBD>',' ',' ',' ', ' ','<EFBFBD>',' ',' ',
'<EFBFBD>',' ',' ','<EFBFBD>', ' ','<EFBFBD>','<EFBFBD>','<EFBFBD>', '<EFBFBD>',' ','<EFBFBD>','<EFBFBD>', '<EFBFBD>',' ','<EFBFBD>',' ',
'<EFBFBD>','<EFBFBD>','<EFBFBD>',' ', '<EFBFBD>',' ',' ',' ', ' ','<EFBFBD>',' ',' ', ' ','<EFBFBD>',' ','<EFBFBD>'
};

88
src/list.c Normal file
View File

@ -0,0 +1,88 @@
/*
* list.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "list.h"
#include "config.h"
int append_list(struct url **urls, char *p){
struct url *q, *t, *u=NULL;
q = *urls;
while(q){
if(strcmp(q->url_str, p) == 0)
return 0;
u = q;
q = q->r;
}
t = createListItem(p);
if(t){
if(*urls == NULL)
*urls = t;
else if(u)
u->r = t;
return 1;
}
return -1;
}
struct url *createListItem(char *s){
struct url *h=NULL;
if((h = malloc(sizeof(struct url))) == NULL)
return NULL;
strncpy(h->url_str, s, URL_LEN-1);
h->r = NULL;
return h;
}
int isOnList(struct url *u, char *item){
struct url *p, *q;
p = u;
while(p != NULL){
q = p->r;
if(p){
if(strcmp(p->url_str, item) == 0) return 1;
}
p = q;
}
return 0;
}
void freeList(struct url *u){
struct url *p, *q;
p = u;
while(p != NULL){
q = p->r;
if(p)
free(p);
p = q;
}
}

16
src/list.h Normal file
View File

@ -0,0 +1,16 @@
/*
* list.h, SJ
*/
#ifndef _LIST_H
#define _LIST_H
#include "defs.h"
int append_list(struct url **urls, char *p);
struct url *createListItem(char *s);
int isOnList(struct url *u, char *item);
void freeList(struct url *u);
#endif /* _LIST_H */

400
src/memc.c Normal file
View File

@ -0,0 +1,400 @@
/*
* memc.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <errno.h>
#include <piler.h>
int __recvtimeout(int s, char *buf, int len, int timeout){
fd_set fds;
int n;
struct timeval tv;
memset(buf, 0, MAXBUFSIZE);
FD_ZERO(&fds);
FD_SET(s, &fds);
tv.tv_sec = 0;
tv.tv_usec = timeout;
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0) return -2; // timeout!
if (n == -1) return -1; // error
return recv(s, buf, len, 0);
}
void memcached_init(struct memcached_server *ptr, char *server_ip, int server_port){
ptr->snd_timeout= 0;
ptr->rcv_timeout= 100000;
ptr->send_size= -1;
ptr->recv_size= -1;
ptr->fd = -1;
ptr->last_read_bytes = 0;
snprintf(ptr->server_ip, IPLEN, "%s", server_ip);
ptr->server_port = server_port;
ptr->initialised = 0;
}
int set_socket_options(struct memcached_server *ptr){
int error, flag=1, flags, rval;
struct timeval waittime;
struct linger linger;
if(ptr->snd_timeout){
waittime.tv_sec = 0;
waittime.tv_usec = ptr->snd_timeout;
error = setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, &waittime, (socklen_t)sizeof(struct timeval));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->rcv_timeout){
waittime.tv_sec = 0;
waittime.tv_usec = ptr->rcv_timeout;
error = setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, &waittime, (socklen_t)sizeof(struct timeval));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->flags.no_block){
linger.l_onoff = 1;
linger.l_linger = 0; /* By default on close() just drop the socket */
error = setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, &linger, (socklen_t)sizeof(struct linger));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->flags.tcp_nodelay){
error = setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, &flag, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->flags.tcp_keepalive){
error= setsockopt(ptr->fd, SOL_SOCKET, SO_KEEPALIVE, &flag, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
/*if(ptr->tcp_keepidle > 0){
error = setsockopt(ptr->fd, IPPROTO_TCP, TCP_KEEPIDLE, &ptr->tcp_keepidle, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}*/
if(ptr->send_size > 0){
error = setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, &ptr->send_size, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
if(ptr->recv_size > 0){
error = setsockopt(ptr->fd, SOL_SOCKET, SO_RCVBUF, &ptr->recv_size, (socklen_t)sizeof(int));
if(error) return MEMCACHED_FAILURE;
}
/* always use nonblocking IO to avoid write deadlocks */
flags = fcntl(ptr->fd, F_GETFL, 0);
if(flags == -1) return MEMCACHED_FAILURE;
if((flags & O_NONBLOCK) == 0){
rval = fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
if(rval == -1) return MEMCACHED_FAILURE;
}
return MEMCACHED_SUCCESS;
}
int memcached_connect(struct memcached_server *ptr){
struct in_addr addr;
if(ptr->last_read_bytes > 0) return MEMCACHED_SUCCESS;
if(ptr->initialised == 0){
ptr->addr.sin_family = AF_INET;
ptr->addr.sin_port = htons(ptr->server_port);
if(inet_aton(ptr->server_ip, &addr) == 0) return MEMCACHED_FAILURE;
ptr->addr.sin_addr.s_addr = addr.s_addr;
bzero(&(ptr->addr.sin_zero), 8);
ptr->initialised = 1;
}
if((ptr->fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ return MEMCACHED_FAILURE; }
if(set_socket_options(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
if(connect(ptr->fd, (struct sockaddr *)&ptr->addr, sizeof(struct sockaddr)) == -1){
if(errno == EINPROGRESS || /* nonblocking mode - first return, */
errno == EALREADY) /* nonblocking mode - subsequent returns */
{
struct pollfd fds[1];
fds[0].fd = ptr->fd;
fds[0].events = POLLOUT;
int error = poll(fds, 1, 1000);
if (error != 1 || fds[0].revents & POLLERR)
{
if (fds[0].revents & POLLERR)
{
int err;
socklen_t len = sizeof (err);
(void)getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &err, &len);
//ptr->cached_errno= (err == 0) ? errno : err;
}
(void)close(ptr->fd);
ptr->fd= -1;
return MEMCACHED_FAILURE;
}
return MEMCACHED_SUCCESS;
}
else if (errno == EISCONN) /* we are connected :-) */
{
return MEMCACHED_SUCCESS;
}
else if (errno != EINTR)
{
(void)close(ptr->fd);
ptr->fd= -1;
}
return MEMCACHED_FAILURE;
}
return MEMCACHED_SUCCESS;
}
int memcached_shutdown(struct memcached_server *ptr){
if(ptr->fd != -1){
close(ptr->fd);
ptr->fd = -1;
}
return MEMCACHED_SUCCESS;
}
int memcached_add(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry){
int len=0;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE-1, "add %s %d %ld %d \r\n", key, flags, expiry, valuelen);
len = strlen(ptr->buf);
strncat(ptr->buf, value, MAXBUFSIZE-1);
strncat(ptr->buf, "\r\n", MAXBUFSIZE-1);
len += valuelen + 2;
send(ptr->fd, ptr->buf, len, 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(strcmp("STORED\r\n", ptr->buf)) return MEMCACHED_FAILURE;
return MEMCACHED_SUCCESS;
}
int memcached_set(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry){
int len=0;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE-1, "set %s %d %ld %d \r\n", key, flags, expiry, valuelen);
len = strlen(ptr->buf);
strncat(ptr->buf, value, MAXBUFSIZE-1);
strncat(ptr->buf, "\r\n", MAXBUFSIZE-1);
len += valuelen + 2;
send(ptr->fd, ptr->buf, len, 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(strcmp("STORED\r\n", ptr->buf)) return MEMCACHED_FAILURE;
return MEMCACHED_SUCCESS;
}
int memcached_increment(struct memcached_server *ptr, char *key, unsigned int keylen, unsigned long long value, unsigned long long *result){
char *p;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE, "incr %s %llu\r\n", key, value);
send(ptr->fd, ptr->buf, strlen(ptr->buf), 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(!strcmp("NOT_FOUND\r\n", ptr->buf)) return MEMCACHED_FAILURE;
p = strchr(ptr->buf, '\r');
if(p){
*p = '\0';
*result = strtoul(ptr->buf, NULL, 10);
}
return MEMCACHED_SUCCESS;
}
char *memcached_get(struct memcached_server *ptr, char *key, unsigned int *len, unsigned int *flags){
int rc;
char *p;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return NULL;
snprintf(ptr->buf, MAXBUFSIZE, "get %s \r\n", key);
rc = send(ptr->fd, ptr->buf, strlen(ptr->buf), 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(ptr->last_read_bytes <= 0){
memcached_shutdown(ptr);
return NULL;
}
if(ptr->last_read_bytes < 10) return NULL;
if(strncmp("VALUE ", ptr->buf, 6)) return NULL;
p = strchr(ptr->buf, '\r');
if(!p) return NULL;
*p = '\0';
ptr->result = p + 2;
p = strrchr(ptr->buf + 6, ' ');
if(!p) return NULL;
*len = atoi(p+1);
*p = '\0';
p = strrchr(ptr->buf + 6, ' ');
if(!p) return NULL;
*flags = atoi(p+1);
*p = '\0';
p = strchr(ptr->result, '\r');
if(!p) return NULL;
*p = '\0';
return ptr->result;
}
int memcached_mget(struct memcached_server *ptr, char *key){
int rc;
if(memcached_connect(ptr) != MEMCACHED_SUCCESS) return MEMCACHED_FAILURE;
snprintf(ptr->buf, MAXBUFSIZE, "get %s \r\n", key);
rc = send(ptr->fd, ptr->buf, strlen(ptr->buf), 0);
ptr->last_read_bytes = __recvtimeout(ptr->fd, ptr->buf, MAXBUFSIZE, ptr->rcv_timeout);
if(ptr->last_read_bytes <= 0){
memcached_shutdown(ptr);
return MEMCACHED_FAILURE;
}
ptr->result = ptr->buf;
return MEMCACHED_SUCCESS;
}
char *memcached_fetch_result(struct memcached_server *ptr, char *key, char *value, unsigned int *flags){
char *p, *q;
int len=0;
if(ptr->last_read_bytes < 10) return NULL;
if(strncmp("VALUE ", ptr->result, 6)) return NULL;
/* first read, eg. 'VALUE aaaa 0 4' */
p = strchr(ptr->result, '\r');
if(!p) return NULL;
*p = '\0';
p = ptr->result + strlen(ptr->result) + 2;
q = strrchr(ptr->result + 6, ' ');
if(!q) return NULL;
len = atoi(q+1);
*q = '\0';
q = strchr(ptr->result + 6, ' ');
if(!q) return NULL;
*q = '\0';
*flags = atoi(q+1);
snprintf(key, MAX_MEMCACHED_KEY_LEN-1, "%s", ptr->result + 6);
/* now read 'len' bytes */
q = p + len;
*q = '\0';
snprintf(value, MAXBUFSIZE-1, "%s", p);
p = q + 2;
ptr->result = p;
return p;
}

19
src/memc.h Normal file
View File

@ -0,0 +1,19 @@
/*
* memc.h, SJ
*/
#ifndef _MEMC_H
#define _MEMC_H
void memcached_init(struct memcached_server *ptr, char *server_ip, int server_port);
int set_socket_options(struct memcached_server *ptr);
int memcached_connect(struct memcached_server *ptr);
int memcached_shutdown(struct memcached_server *ptr);
int memcached_add(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry);
int memcached_set(struct memcached_server *ptr, char *key, unsigned int keylen, char *value, unsigned int valuelen, unsigned int flags, unsigned long expiry);
int memcached_increment(struct memcached_server *ptr, char *key, unsigned int keylen, unsigned long long value, unsigned long long *result);
char *memcached_get(struct memcached_server *ptr, char *key, unsigned int *len, unsigned int *flags);
int memcached_mget(struct memcached_server *ptr, char *key);
char *memcached_fetch_result(struct memcached_server *ptr, char *key, char *value, unsigned int *flags);
#endif /* _MEMC_H */

204
src/memcached.c Normal file
View File

@ -0,0 +1,204 @@
/*
* memcached.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <syslog.h>
#include <piler.h>
int getUserdataFromMemcached(struct session_data *sdata, struct __data *data, char *email, struct __config *cfg){
unsigned int len=0;
uint32_t flags = 0;
char key[SMALLBUFSIZE], *s=NULL, *p;
//if(data->memc.initialised == 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:%s", MEMCACHED_CLAPF_PREFIX, email);
s = memcached_get(&(data->memc), key, &len, &flags);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached user query=%s, data=%s (%d)", sdata->ttmpfile, key, s, len);
if(len > 0){
/* 1000:8:sj:acts.hu:1 */
if(len == 1 && s[0] == 'U'){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: %s is unknown", sdata->ttmpfile, email);
return 1;
}
p = strchr(s, ':');
if(p){ *p = '\0'; sdata->uid = atol(s); s = p+1; }
p = strchr(s, ':');
if(p){ *p = '\0'; sdata->gid = atol(s); s = p+1; }
p = strchr(s, ':');
if(p){ *p = '\0'; snprintf(sdata->name, SMALLBUFSIZE-1, "%s", s); s = p+1; }
p = strchr(s, ':');
if(p){ *p = '\0'; snprintf(sdata->domain, SMALLBUFSIZE-1, "%s", s); s = p+1; }
sdata->policy_group = atoi(s);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached parsed user data: uid: %ld, gid: %ld, name: %s, domain: %s, policy group: %d", sdata->ttmpfile, sdata->uid, sdata->gid, sdata->name, sdata->domain, sdata->policy_group);
return 1;
}
return 0;
}
int putUserdataToMemcached(struct session_data *sdata, struct __data *data, char *email, struct __config *cfg){
uint32_t flags = 0;
char key[SMALLBUFSIZE], value[SMALLBUFSIZE];
snprintf(key, SMALLBUFSIZE-1, "%s:%s", MEMCACHED_CLAPF_PREFIX, email);
if(sdata->uid == 0)
strcpy(value, "U");
else
snprintf(value, SMALLBUFSIZE-1, "%ld:%ld:%s:%s:%d", sdata->uid, sdata->gid, sdata->name, sdata->domain, sdata->policy_group);
if(memcached_add(&(data->memc), key, strlen(key), value, strlen(value), cfg->memcached_ttl, flags) == MEMCACHED_SUCCESS) return 1;
return 0;
}
int getPolicyFromMemcached(struct session_data *sdata, struct __data *data, struct __config *cfg, struct __config *my_cfg){
unsigned int len=0;
uint32_t flags = 0;
char key[SMALLBUFSIZE], *s=NULL, *p;
if(sdata->policy_group <= 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:%d", MEMCACHED_CLAPF_PREFIX, sdata->policy_group);
s = memcached_get(&(data->memc), key, &len, &flags);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached policy query=%s, data=%s (%d)", sdata->ttmpfile, key, s, len);
if(len > 0){
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->deliver_infected_email = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->silently_discard_infected_email = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->use_antispam = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; snprintf(my_cfg->spam_subject_prefix, MAXVAL-1, "%s", s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->enable_auto_white_list = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->max_message_size_to_filter = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; snprintf(my_cfg->rbl_domain, MAXVAL-1, "%s", s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; snprintf(my_cfg->surbl_domain, MAXVAL-1, "%s", s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->spam_overall_limit = atof(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->spaminess_oblivion_limit = atof(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->replace_junk_characters = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->invalid_junk_limit = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->invalid_junk_line = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->penalize_images = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->penalize_embed_images = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->penalize_octet_stream = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->training_mode = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->initial_1000_learning = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->store_metadata = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->store_only_spam = atoi(s); s = p+1; }
p = strchr(s, ':'); if(p){ *p = '\0'; my_cfg->message_from_a_zombie = atoi(s); }
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached parsed policy data: spam limit: %.4f, oblivion: %.4f, subject prefix: *%s*, rbl: *%s*, training mode: %d, meta: %d",
sdata->ttmpfile, my_cfg->spam_overall_limit, my_cfg->spaminess_oblivion_limit, my_cfg->spam_subject_prefix, my_cfg->rbl_domain, my_cfg->training_mode, my_cfg->store_metadata);
return 1;
}
return 0;
}
int putPolicyToMemcached(struct session_data *sdata, struct __data *data, struct __config *my_cfg){
uint32_t flags = 0;
char key[SMALLBUFSIZE], value[SMALLBUFSIZE];
if(sdata->policy_group <= 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:%d", MEMCACHED_CLAPF_PREFIX, sdata->policy_group);
snprintf(value, SMALLBUFSIZE-1, "%d:%d:%d:%s:%d:%ld:%s:%s:%.4f:%.4f:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
my_cfg->deliver_infected_email,
my_cfg->silently_discard_infected_email,
my_cfg->use_antispam,
my_cfg->spam_subject_prefix,
my_cfg->enable_auto_white_list,
my_cfg->max_message_size_to_filter,
my_cfg->rbl_domain,
my_cfg->surbl_domain,
my_cfg->spam_overall_limit,
my_cfg->spaminess_oblivion_limit,
my_cfg->replace_junk_characters,
my_cfg->invalid_junk_limit,
my_cfg->invalid_junk_line,
my_cfg->penalize_images,
my_cfg->penalize_embed_images,
my_cfg->penalize_octet_stream,
my_cfg->training_mode,
my_cfg->initial_1000_learning,
my_cfg->store_metadata,
my_cfg->store_only_spam,
my_cfg->message_from_a_zombie
);
if(memcached_add(&(data->memc), key, strlen(key), value, strlen(value), my_cfg->memcached_ttl, flags) == MEMCACHED_SUCCESS) return 1;
return 0;
}
int getWBLFromMemcached(struct session_data *sdata, struct __data *data, struct __config *cfg){
unsigned int len=0;
uint32_t flags = 0;
char key[SMALLBUFSIZE], *s=NULL, *p;
snprintf(key, SMALLBUFSIZE-1, "%s:wbl%ld", MEMCACHED_CLAPF_PREFIX, sdata->uid);
s = memcached_get(&(data->memc), key, &len, &flags);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: memcached wbl query=%s, data=%s (%d)", sdata->ttmpfile, key, s, len);
if(len > 0){
/* whiteemail1,whiteemail2:blackemail1,blackemail2 */
p = strchr(s, ':');
if(p){
*p = '\0';
snprintf(sdata->whitelist, MAXBUFSIZE-1, "%s", s);
snprintf(sdata->blacklist, MAXBUFSIZE-1, "%s", p+1);
}
return 1;
}
return 0;
}
int putWBLToMemcached(struct session_data *sdata, struct __data *data, struct __config *cfg){
uint32_t flags = 0;
char key[SMALLBUFSIZE], value[2*MAXBUFSIZE];
if(sdata->uid <= 0) return 0;
snprintf(key, SMALLBUFSIZE-1, "%s:wbl%ld", MEMCACHED_CLAPF_PREFIX, sdata->uid);
snprintf(value, 2*MAXBUFSIZE-1, "%s:%s", sdata->whitelist, sdata->blacklist);
if(memcached_add(&(data->memc), key, strlen(key), value, strlen(value), cfg->memcached_ttl, flags) == MEMCACHED_SUCCESS) return 1;
return 0;
}

257
src/message.c Normal file
View File

@ -0,0 +1,257 @@
/*
* message.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <piler.h>
#include <zlib.h>
int is_existing_message_id(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc=0;
char s[SMALLBUFSIZE];
MYSQL_STMT *stmt;
MYSQL_BIND bind[1];
my_bool is_null[1];
unsigned long len=0;
stmt = mysql_stmt_init(&(sdata->mysql));
if(!stmt){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_init() error", sdata->ttmpfile, SQL_METADATA_TABLE);
goto ENDE;
}
snprintf(s, SMALLBUFSIZE-1, "SELECT message_id FROM %s WHERE message_id=?", SQL_METADATA_TABLE);
if(mysql_stmt_prepare(stmt, s, strlen(s))){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_prepare() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = state->message_id;
bind[0].is_null = 0;
len = strlen(state->message_id); bind[0].length = &len;
if(mysql_stmt_bind_param(stmt, bind)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_param() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
if(mysql_stmt_execute(stmt)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_execute() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = &s[0];
bind[0].buffer_length = sizeof(s)-1;
bind[0].is_null = &is_null[0];
bind[0].length = &len;
if(mysql_stmt_bind_result(stmt, bind)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_result() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
if(mysql_stmt_store_result(stmt)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_store_result() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE;
}
if(!mysql_stmt_fetch(stmt)){
syslog(LOG_PRIORITY, "%s: found message_id:*%s*(%ld) null=%d", sdata->ttmpfile, s, len, is_null[0]);
if(is_null[0] == 0) rc = 1;
}
mysql_stmt_close(stmt);
ENDE:
return rc;
}
int is_body_digest_already_stored(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc=0;
char s[SMALLBUFSIZE];
MYSQL_RES *res;
MYSQL_ROW row;
snprintf(s, SMALLBUFSIZE-1, "SELECT `bodydigest` FROM `%s` WHERE `bodydigest`='%s'", SQL_METADATA_TABLE, sdata->bodydigest);
//if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: check for body digest sql: *%s*", sdata->ttmpfile, s);
if(mysql_real_query(&(sdata->mysql), s, strlen(s)) == 0){
res = mysql_store_result(&(sdata->mysql));
if(res != NULL){
row = mysql_fetch_row(res);
if(row) rc = 1;
mysql_free_result(res);
}
}
return rc;
}
int hand_to_sphinx(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc;
char s[BIGBUFSIZE+2*MAXBUFSIZE];
snprintf(s, sizeof(s)-1, "INSERT INTO %s (`from`, `to`, `subject`, `body`, `arrived`, `sent`, `size`, `piler_id`) values('%s','%s','%s','%s',%ld,%ld,%d,'%s')", SQL_SPHINX_TABLE, state->b_from, state->b_to, state->b_subject, state->b_body, sdata->now, sdata->sent, sdata->tot_len, sdata->ttmpfile);
rc = mysql_real_query(&(sdata->mysql), s, strlen(s));
if(rc == 0) return OK;
syslog(LOG_PRIORITY, "%s: sphinx sql error: *%s*", sdata->ttmpfile, mysql_error(&(sdata->mysql)));
return ERR;
}
int store_meta_data(struct session_data *sdata, struct _state *state, struct __config *cfg){
int i=0, rc, ret=ERR;
char *p, s[MAXBUFSIZE], s2[SMALLBUFSIZE];
struct url *list = NULL;
MYSQL_STMT *stmt;
MYSQL_BIND bind[4];
unsigned long len[4];
stmt = mysql_stmt_init(&(sdata->mysql));
if(!stmt){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_init() error", sdata->ttmpfile, SQL_METADATA_TABLE);
goto ENDE_META;
}
snprintf(s, MAXBUFSIZE-1, "INSERT INTO %s (`from`,`to`,`subject`,`arrived`,`sent`,`size`,`hlen`,`piler_id`,`message_id`,`bodydigest`) VALUES(?,?,?,%ld,%ld,%d,%d,'%s',?,'%s')", SQL_METADATA_TABLE, sdata->now, sdata->sent, sdata->tot_len, sdata->hdr_len, sdata->ttmpfile, sdata->bodydigest);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: meta sql: *%s*", sdata->ttmpfile, s);
if(mysql_stmt_prepare(stmt, s, strlen(s))){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_prepare() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE_META;
}
if(strlen(state->b_to) < 5){
snprintf(s2, sizeof(s2)-1, "undisclosed-recipients");
p = NULL;
goto LABEL1;
}
else p = state->b_to;
do {
p = split_str(p, " ", s2, sizeof(s2)-1);
if(strlen(s2) > 5){
LABEL1:
if(isOnList(list, s2) == 1) continue;
append_list(&list, s2);
i++;
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = state->b_from;
bind[0].is_null = 0;
len[0] = strlen(state->b_from); bind[0].length = &len[0];
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = s2;
bind[1].is_null = 0;
len[1] = strlen(s2); bind[1].length = &len[1];
bind[2].buffer_type = MYSQL_TYPE_STRING;
bind[2].buffer = state->b_subject;
bind[2].is_null = 0;
len[2] = strlen(state->b_subject); bind[2].length = &len[2];
bind[3].buffer_type = MYSQL_TYPE_STRING;
bind[3].buffer = state->message_id;
bind[3].is_null = 0;
len[3] = strlen(state->message_id); bind[3].length = &len[3];
if(mysql_stmt_bind_param(stmt, bind)){
syslog(LOG_PRIORITY, "%s: %s.mysql_stmt_bind_param() error: %s", sdata->ttmpfile, SQL_METADATA_TABLE, mysql_stmt_error(stmt));
goto ENDE_META;
}
rc = mysql_stmt_execute(stmt);
//rc = mysql_real_query(&(sdata->mysql), s, strlen(s));
if(rc){
syslog(LOG_PRIORITY, "%s: meta sql error: *%s*", sdata->ttmpfile, mysql_error(&(sdata->mysql)));
ret = ERR_EXISTS;
goto ENDE_META;
}
}
} while(p);
if(i == 0) ret = ERR_EXISTS;
else ret = OK;
ENDE_META:
freeList(list);
return ret;
}
int processMessage(struct session_data *sdata, struct _state *state, struct __config *cfg){
int rc;
/* discard if existing message_id */
if(is_existing_message_id(sdata, state, cfg) == 1){
return ERR_EXISTS;
}
/* check for existing body digest */
rc = is_body_digest_already_stored(sdata, state, cfg);
rc = store_message(sdata, state, rc, cfg);
rc = store_meta_data(sdata, state, cfg);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored metadata, rc=%d", sdata->ttmpfile, rc);
if(rc == ERR_EXISTS) return ERR_EXISTS;
rc = hand_to_sphinx(sdata, state, cfg);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: stored indexdata, rc=%d", sdata->ttmpfile, rc);
return OK;
}

432
src/misc.c Normal file
View File

@ -0,0 +1,432 @@
/*
* misc.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <ctype.h>
#include "misc.h"
#include "smtpcodes.h"
#include "errmsg.h"
#include "config.h"
#include "tai.h"
/*
* fatal function for quitting
*/
void __fatal(char *s){
fprintf(stderr, "%s\n", s);
exit(1);
}
/*
* calculate the difference betwwen two timevals in [usec]
*/
long tvdiff(struct timeval a, struct timeval b){
double res;
res = (a.tv_sec * 1000000 + a.tv_usec) - (b.tv_sec * 1000000 + b.tv_usec);
return (long) res;
}
/*
* search something in a buffer
*/
int searchStringInBuffer(char *s, int len1, char *what, int len2){
int i, k, r;
for(i=0; i<len1; i++){
r = 0;
for(k=0; k<len2; k++){
if(*(s+i+k) == *(what+k))
r++;
}
if(r == len2)
return i;
}
return 0;
}
/*
* count a character in buffer
*/
int countCharacterInBuffer(char *p, char c){
int i=0;
for(; *p; p++){
if(*p == c)
i++;
}
return i;
}
void replaceCharacterInBuffer(char *p, char from, char to){
int i, k=0;
for(i=0; i<strlen(p); i++){
if(p[i] == from){
if(to > 0){
p[k] = to;
k++;
}
}
else {
p[k] = p[i];
k++;
}
}
p[k] = '\0';
}
/*
* split a string by a character as delimiter
*/
char *split(char *row, int ch, char *s, int size){
char *r;
int len;
if(row == NULL)
return NULL;
r = strchr(row, ch);
if(r == NULL){
len = strlen(row);
if(len > size)
len = size;
}
else {
len = strlen(row) - strlen(r);
if(len > size)
len = size;
r++;
}
if(s != NULL){
strncpy(s, row, len);
s[len] = '\0';
}
return r;
}
/*
* split a string by a string as delimiter
*/
char *split_str(char *row, char *what, char *s, int size){
char *r;
int len;
memset(s, 0, size);
if(row == NULL)
return NULL;
r = strstr(row, what);
if(r == NULL){
len = strlen(row);
if(len > size)
len = size;
}
else {
len = strlen(row) - strlen(r);
if(len > size)
len = size;
r += strlen(what);
}
if(s != NULL){
strncpy(s, row, len);
s[len] = '\0';
}
return r;
}
/*
* trim trailing CR-LF
*/
void trimBuffer(char *s){
char *p;
p = strrchr(s, '\n');
if(p) *p = '\0';
p = strrchr(s, '\r');
if(p) *p = '\0';
}
/*
* extract email
*/
int extractEmail(char *rawmail, char *email){
char *p;
p = strchr(rawmail, '<');
if(p){
snprintf(email, SMALLBUFSIZE-1, "%s", p+1);
p = strchr(email, '>');
if(p){
*p = '\0';
return 1;
}
}
return 0;
}
/*
* create an ID
*/
void create_id(char *id){
int i;
unsigned char buf[RND_STR_LEN/2];
memset(id, 0, SMALLBUFSIZE);
get_random_bytes(buf, RND_STR_LEN/2);
for(i=0; i < RND_STR_LEN/2; i++){
sprintf(id, "%02x", buf[i]);
id += 2;
}
}
/*
* reading from pool
*/
int get_random_bytes(unsigned char *buf, int len){
int fd, ret=0;
struct taia now;
char nowpack[TAIA_PACK];
/* the first 12 bytes are the taia timestamp */
taia_now(&now);
taia_pack(nowpack, &now);
memcpy(buf, nowpack, 12);
fd = open(RANDOM_POOL, O_RDONLY);
if(fd == -1) return ret;
if(readFromEntropyPool(fd, buf+12, len-12) != len-12){
syslog(LOG_PRIORITY, "%s: %s", ERR_CANNOT_READ_FROM_POOL, RANDOM_POOL);
}
close(fd);
return ret;
}
/*
* read random data from entropy pool
*/
int readFromEntropyPool(int fd, void *_s, size_t n){
char *s = _s;
ssize_t res, pos = 0;
while(n > pos){
res = read(fd, s + pos, n - pos);
switch(res){
case -1: continue;
case 0: return res;
default : pos += res;
}
}
return pos;
}
/*
* recv() with timeout
*/
int recvtimeout(int s, char *buf, int len, int timeout){
fd_set fds;
int n;
struct timeval tv;
memset(buf, 0, MAXBUFSIZE);
FD_ZERO(&fds);
FD_SET(s, &fds);
tv.tv_sec = timeout;
tv.tv_usec = TIMEOUT_USEC;
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0) return -2; // timeout!
if (n == -1) return -1; // error
return recv(s, buf, len, 0);
}
/*
* check if it's a valid ID
*/
int isValidClapfID(char *p){
if(strlen(p) != 30 && strlen(p) != 31)
return 0;
for(; *p; p++){
/* 0-9: 0x30-0x39, a-f: 0x61-0x66 */
if(! ((*p >= 0x30 && *p <= 0x39) || (*p >= 0x61 && *p <= 0x66) || *p == 0x0d) ){
//printf("%c*\n", *p);
return 0;
}
}
return 1;
}
/*
* is it a valid dotted IPv4 address
*/
int isDottedIPv4Address(char *s){
struct in_addr addr;
if(inet_aton(s, &addr) == 0) return 0;
return 1;
}
/*
* whitelist check
*/
int isEmailAddressOnList(char *list, char *tmpfile, char *email, struct __config *cfg){
char *p, *q, w[SMALLBUFSIZE];
if(email == NULL) return 0;
p = list;
if(cfg->verbosity >= _LOG_INFO) syslog(LOG_PRIORITY, "%s: list: %s", tmpfile, list);
do {
p = split(p, '\n', w, SMALLBUFSIZE-1);
trimBuffer(w);
if(strlen(w) > 2){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: matching '%s' on '%s'", tmpfile, w, email);
if(w[strlen(w)-1] == '$'){
q = email + strlen(email) - strlen(w) + 1;
if(strncasecmp(q, w, strlen(w)-1) == 0)
return 1;
}
else if(strcasestr(email, w))
return 1;
}
} while(p);
return 0;
}
void write_pid_file(char *pidfile){
FILE *f;
f = fopen(pidfile, "w");
if(f){
fprintf(f, "%d", (int)getpid());
fclose(f);
}
else syslog(LOG_PRIORITY, "cannot write pidfile: %s", pidfile);
}
int drop_privileges(struct passwd *pwd){
if(pwd->pw_uid > 0 && pwd->pw_gid > 0){
if(getgid() != pwd->pw_gid){
if(setgid(pwd->pw_gid)) return -1;
}
if(getuid() != pwd->pw_uid){
if(setuid(pwd->pw_uid)) return -1;
}
}
return 0;
}
#ifndef _GNU_SOURCE
char *strcasestr(const char *s, const char *find){
char c, sc;
size_t len;
if((c = *find++) != 0){
c = tolower((unsigned char)c);
len = strlen(find);
do {
do {
if((sc = *s++) == 0)
return (NULL);
} while((char)tolower((unsigned char)sc) != c);
} while (strncasecmp(s, find, len) != 0);
s--;
}
return((char*)s);
}
#endif

39
src/misc.h Normal file
View File

@ -0,0 +1,39 @@
/*
* misc.h, SJ
*/
#ifndef _MISC_H
#define _MISC_H
#include <sys/time.h>
#include <pwd.h>
#include <cfg.h>
#include "defs.h"
void __fatal(char *s);
long tvdiff(struct timeval a, struct timeval b);
int searchStringInBuffer(char *s, int len1, char *what, int len2);
int countCharacterInBuffer(char *p, char c);
void replaceCharacterInBuffer(char *p, char from, char to);
char *split(char *row, int ch, char *s, int size);
char *split_str(char *row, char *what, char *s, int size);
void trimBuffer(char *s);
int extractEmail(char *rawmail, char *email);
void create_id(char *id);
int get_random_bytes(unsigned char *buf, int len);
int readFromEntropyPool(int fd, void *_s, size_t n);
int recvtimeout(int s, char *buf, int len, int timeout);
int isValidClapfID(char *p);
int isDottedIPv4Address(char *s);
int isEmailAddressOnList(char *list, char *tmpfile, char *email, struct __config *cfg);
void write_pid_file(char *pidfile);
int drop_privileges(struct passwd *pwd);
#ifndef _GNU_SOURCE
char *strcasestr(const char *s, const char *find);
#endif
#endif /* _MISC_H */

403
src/parser.c Normal file
View File

@ -0,0 +1,403 @@
/*
* parser.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <piler.h>
struct _state parseMessage(struct session_data *sdata, struct __config *cfg){
FILE *f;
char buf[MAXBUFSIZE];
struct _state state;
initState(&state);
f = fopen(sdata->ttmpfile, "r");
if(!f){
syslog(LOG_PRIORITY, "%s: cannot open", sdata->ttmpfile);
return state;
}
while(fgets(buf, MAXBUFSIZE-1, f)){
parseLine(buf, &state, sdata, cfg);
}
fclose(f);
free_boundary(state.boundaries);
if(state.message_id[0] == 0) snprintf(state.message_id, SMALLBUFSIZE-1, "null");
if(state.b_from[strlen(state.b_from)-1] == ' ') state.b_from[strlen(state.b_from)-1] = '\0';
if(state.b_to[strlen(state.b_to)-1] == ' ') state.b_to[strlen(state.b_to)-1] = '\0';
if(state.b_subject[strlen(state.b_subject)-1] == ' ') state.b_subject[strlen(state.b_subject)-1] = '\0';
make_body_digest(sdata);
syslog(LOG_PRIORITY, "%s: from=%s, to=%s, subj=%s, message-id=%s", sdata->ttmpfile, state.b_from, state.b_to, state.b_subject, state.message_id);
return state;
}
int parseLine(char *buf, struct _state *state, struct session_data *sdata, struct __config *cfg){
char *p, *q, puf[MAXBUFSIZE], muf[MAXBUFSIZE], u[SMALLBUFSIZE], token[MAX_TOKEN_LEN];
int x, b64_len, boundary_line=0;
memset(token, 0, MAX_TOKEN_LEN);
state->line_num++;
if(*buf == '.' && *(buf+1) == '.') buf++;
/* undefined message state */
if(state->is_header == 1 && buf[0] != ' ' && buf[0] != '\t' && strchr(buf, ':')) state->message_state = MSG_UNDEF;
/* skip empty lines */
if(state->message_rfc822 == 0 && (buf[0] == '\r' || buf[0] == '\n') ){
state->message_state = MSG_BODY;
if(state->is_header == 1) state->is_header = 0;
return 0;
}
trimBuffer(buf);
/* skip the first line, if it's a "From <email address> date" format */
if(state->line_num == 1 && strncmp(buf, "From ", 5) == 0) return 0;
if(state->is_header == 0 && buf[0] != ' ' && buf[0] != '\t') state->message_state = MSG_BODY;
if((state->content_type_is_set == 0 || state->is_header == 1) && strncasecmp(buf, "Content-Type:", strlen("Content-Type:")) == 0) state->message_state = MSG_CONTENT_TYPE;
else if(strncasecmp(buf, "Content-Transfer-Encoding:", strlen("Content-Transfer-Encoding:")) == 0) state->message_state = MSG_CONTENT_TRANSFER_ENCODING;
else if(strncasecmp(buf, "Content-Disposition:", strlen("Content-Disposition:")) == 0) state->message_state = MSG_CONTENT_DISPOSITION;
if(state->message_state == MSG_CONTENT_TYPE || state->message_state == MSG_CONTENT_TRANSFER_ENCODING) state->is_header = 1;
/* header checks */
if(state->is_header == 1){
if(strncasecmp(buf, "Received: from ", strlen("Received: from ")) == 0) state->message_state = MSG_RECEIVED;
else if(strncasecmp(buf, "From:", strlen("From:")) == 0) state->message_state = MSG_FROM;
else if(strncasecmp(buf, "To:", 3) == 0) state->message_state = MSG_TO;
else if(strncasecmp(buf, "Cc:", 3) == 0) state->message_state = MSG_CC;
else if(strncasecmp(buf, "Message-Id:", 11) == 0) state->message_state = MSG_MESSAGE_ID;
else if(strncasecmp(buf, "Subject:", strlen("Subject:")) == 0) state->message_state = MSG_SUBJECT;
else if(strncasecmp(buf, "Date:", strlen("Date:")) == 0 && sdata->sent == 0) sdata->sent = parse_date_header(buf);
if(state->message_state == MSG_SUBJECT){
p = &buf[0];
if(strncmp(buf, "Subject:", strlen("Subject:")) == 0) p = &buf[8];
if(*p == ' ') p++;
}
if(state->message_state == MSG_MESSAGE_ID && state->message_id[0] == 0){
p = strchr(buf+11, ' ');
if(p) p = buf + 12;
else p = buf + 11;
snprintf(state->message_id, SMALLBUFSIZE-1, "%s", p);
}
if(state->message_state == MSG_FROM){
p = strchr(buf+5, ' ');
if(p) p = buf + 6;
else p = buf + 5;
snprintf(state->from, SMALLBUFSIZE-1, "FROM*%s", p);
}
/* we are interested in only From:, To:, Subject:, Received:, Content-*: header lines */
if(state->message_state <= 0) return 0;
}
if((p = strcasestr(buf, "boundary"))){
x = extract_boundary(p, state);
}
/* Content-type: checking */
if(state->message_state == MSG_CONTENT_TYPE){
state->message_rfc822 = 0;
/* extract Content type */
p = strchr(buf, ':');
if(p){
p++;
if(*p == ' ' || *p == '\t') p++;
snprintf(state->attachments[state->n_attachments].type, SMALLBUFSIZE-1, "%s", p);
state->content_type_is_set = 1;
p = strchr(state->attachments[state->n_attachments].type, ';');
if(p) *p = '\0';
}
p = strstr(buf, "name=");
if(p){
snprintf(state->attachments[state->n_attachments].filename, SMALLBUFSIZE-1, "%s", p);
}
if(strcasestr(buf, "text/plain") ||
strcasestr(buf, "multipart/mixed") ||
strcasestr(buf, "multipart/alternative") ||
strcasestr(buf, "multipart/report") ||
strcasestr(buf, "message/delivery-status") ||
strcasestr(buf, "text/rfc822-headers") ||
strcasestr(buf, "message/rfc822") ||
strcasestr(buf, "application/ms-tnef")
){
state->textplain = 1;
}
else if(strcasestr(buf, "text/html")){
state->texthtml = 1;
}
/* switch (back) to header mode if we encounterd an attachment with
"message/rfc822" content-type, 2010.05.16, SJ */
if(strcasestr(buf, "message/rfc822")){
state->message_rfc822 = 1;
state->is_header = 1;
}
if(strcasestr(buf, "application/octet-stream")) state->octetstream = 1;
if(strcasestr(buf, "charset") && strcasestr(buf, "UTF-8")) state->utf8 = 1;
extractNameFromHeaderLine(buf, "name", state->attachments[state->n_attachments].filename);
/*if(strlen(state->attachments[state->n_attachments].filename) > 5){
state->has_to_dump = 1;
snprintf(u, sizeof(u)-1, "%s.%d", sdata->ttmpfile, state->n_attachments);
state->fd = open(u, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
}*/
}
if(state->message_state == MSG_CONTENT_DISPOSITION && state->attachments[state->n_attachments].filename[0] == 0)
extractNameFromHeaderLine(buf, "name", state->attachments[state->n_attachments].filename);
if(state->message_state > 0 && state->message_state <= MSG_SUBJECT && state->message_rfc822 == 1) state->message_rfc822 = 0;
/* check for textual base64 encoded part, 2005.03.25, SJ */
if(state->message_state == MSG_CONTENT_TRANSFER_ENCODING){
if(strcasestr(buf, "base64")){
state->base64 = 1;
state->has_base64 = 1;
}
if(strcasestr(buf, "quoted-printable")) state->qp = 1;
if(strcasestr(buf, "image"))
state->num_of_images++;
if(strcasestr(buf, "msword"))
state->num_of_msword++;
}
/* skip the boundary itself */
boundary_line = is_boundary(state->boundaries, buf);
if(!strstr(buf, "boundary=") && !strstr(buf, "boundary =") && boundary_line == 1){
state->content_type_is_set = 0;
/*if(state->has_to_dump == 1){
if(state->fd != -1) close(state->fd);
state->fd = -1;
}*/
if(state->n_attachments < MAX_ATTACHMENTS-1) state->n_attachments++;
state->has_to_dump = 0;
state->base64 = 0; state->textplain = 0; state->texthtml = state->octetstream = 0;
state->skip_html = 0;
state->utf8 = 0;
state->qp = 0;
state->realbinary = 0;
return 0;
}
if(boundary_line == 1){ return 0; }
/* end of boundary check */
/* skip non textual stuff */
if(state->message_state == MSG_BODY){
/*if(state->has_to_dump == 1 && state->fd != -1){
write(state->fd, buf, strlen(buf));
}*/
if(state->base64 == 1) state->attachments[state->n_attachments].size += strlen(buf) / BASE64_RATIO;
else state->attachments[state->n_attachments].size += strlen(buf);
}
if(state->message_state == MSG_BODY && strlen(buf) < 2) return 0;
/*
* sometimes spammers screw up their junk messages, and
* use "application/octet-stream" type for textual parts.
* Now clapf checks whether the attachment is really
* binary. If it has no non-printable characters in a
* base64 encoded line, then let's tokenize it.
*
* Note: in this case we cannot expect fully compliant
* message part. However this case should be very rare
* since legitim messages use proper mime types.
*
* 2010.10.23, SJ
*/
if(state->message_state == MSG_BODY && state->realbinary == 0 && state->octetstream == 1){
snprintf(puf, MAXBUFSIZE-1, "%s", buf);
if(state->base64 == 1) decodeBase64(puf);
if(state->qp == 1) decodeQP(puf);
state->realbinary += countNonPrintableCharacters(puf);
}
if(state->is_header == 0 && state->textplain == 0 && state->texthtml == 0 && (state->message_state == MSG_BODY || state->message_state == MSG_CONTENT_DISPOSITION) && (state->octetstream == 0 || state->realbinary > 0) ) return 0;
/* base64 decode buffer */
if(state->base64 == 1 && state->message_state == MSG_BODY) b64_len = decodeBase64(buf);
/* fix encoded From:, To: and Subject: lines, 2008.11.24, SJ */
if(state->message_state == MSG_FROM || state->message_state == MSG_TO || state->message_state == MSG_CC || state->message_state == MSG_SUBJECT) fixupEncodedHeaderLine(buf);
/* fix soft breaks with quoted-printable decoded stuff, 2006.03.01, SJ */
if(state->qp == 1) fixupSoftBreakInQuotedPritableLine(buf, state);
/* fix base64 stuff if the line does not end with a line break, 2006.03.01, SJ */
if(state->base64 == 1 && state->message_state == MSG_BODY) fixupBase64EncodedLine(buf, state);
if(state->texthtml == 1 && state->message_state == MSG_BODY) markHTML(buf, state);
if(state->message_state == MSG_BODY){
if(state->qp == 1) decodeQP(buf);
if(state->utf8 == 1) decodeUTF8(buf);
}
decodeURL(buf);
if(state->texthtml == 1) decodeHTML(buf);
translateLine((unsigned char*)buf, state);
reassembleToken(buf);
if(state->is_header == 1) p = strchr(buf, ' ');
else p = buf;
//if(strlen(buf) > 3) printf("%d original: %s\n", state->message_state, buf);
do {
p = split(p, DELIMITER, puf, MAXBUFSIZE-1);
if(strcasestr(puf, "http://") || strcasestr(puf, "https://")){
q = puf;
do {
q = split_str(q, "http://", u, SMALLBUFSIZE-1);
if(u[strlen(u)-1] == '.') u[strlen(u)-1] = '\0';
if(strlen(u) > 2 && strncasecmp(u, "www.w3.org", 10) && strchr(u, '.') ){
snprintf(muf, MAXBUFSIZE-1, "http://%s", u);
fixURL(muf);
strncat(muf, " ", MAXBUFSIZE-1);
strncat(state->b_body, muf, BIGBUFSIZE-1);
}
} while(q);
continue;
}
if(state->message_state != MSG_SUBJECT && (strlen(puf) < MIN_WORD_LEN || (strlen(puf) > MAX_WORD_LEN && state->message_state != MSG_FROM && state->message_state != MSG_TO && state->message_state != MSG_CC) || isHexNumber(puf)))
continue;
if(strlen(puf) < 2 || strncmp(puf, "HTML*", 5) == 0) continue;
if(state->message_state == MSG_CONTENT_TYPE && strncmp(puf, "content-type", 12) == 0) continue;
if(state->message_state == MSG_CONTENT_DISPOSITION && strncmp(puf, "content-disposition", 19) == 0) continue;
if(state->message_state == MSG_CONTENT_TRANSFER_ENCODING && strncmp(puf, "content-transfer-encoding", 25) == 0) continue;
degenerateToken((unsigned char*)puf);
strncat(puf, " ", MAXBUFSIZE-1);
if(state->message_state == MSG_SUBJECT)
strncat(state->b_subject, puf, MAXBUFSIZE-1);
else if(state->message_state == MSG_FROM && strchr(puf, '@'))
strncat(state->b_from, puf, SMALLBUFSIZE-1);
else if(state->message_state == MSG_TO && strchr(puf, '@'))
strncat(state->b_to, puf, SMALLBUFSIZE-1);
else if(state->message_state == MSG_CC && strchr(puf, '@'))
strncat(state->b_to, puf, SMALLBUFSIZE-1);
else if(state->is_header == 0)
strncat(state->b_body, puf, BIGBUFSIZE-1);
} while(p);
return 0;
}

41
src/parser.h Normal file
View File

@ -0,0 +1,41 @@
/*
* parser.h, SJ
*/
#ifndef _PARSER_H
#define _PARSER_H
#include "cfg.h"
#include "config.h"
#include "defs.h"
struct _state parseMessage(struct session_data *sdata, struct __config *cfg);
struct _state parseBuffer(struct session_data *sdata, struct __config *cfg);
int parseLine(char *buf, struct _state *state, struct session_data *sdata, struct __config *cfg);
void initState(struct _state *state);
void freeState(struct _state *state);
int extract_boundary(char *p, struct _state *state);
int extractNameFromHeaderLine(char *s, char *name, char *resultbuf);
int attachment_by_type(struct _state *state, char *type);
void fixupEncodedHeaderLine(char *buf);
void fixupSoftBreakInQuotedPritableLine(char *buf, struct _state *state);
void fixupBase64EncodedLine(char *buf, struct _state *state);
void markHTML(char *buf, struct _state *state);
int appendHTMLTag(char *buf, char *htmlbuf, int pos, struct _state *state);
void fixupHTML(char *buf, struct _state *state, struct __config *cfg);
int isSkipHTMLTag(char *s);
void translateLine(unsigned char *p, struct _state *state);
void reassembleToken(char *p);
void degenerateToken(unsigned char *p);
int countInvalidJunkLines(char *p);
int countInvalidJunkCharacters(char *p, int replace_junk);
int countNonPrintableCharacters(char *p);
int isHexNumber(char *p);
void fixURL(char *url);
void fixFQDN(char *fqdn);
void getTLDFromName(char *name);
int isItemOnList(char *item, char *list, char *extralist);
unsigned long parse_date_header(char *s);
#endif /* _PARSER_H */

836
src/parser_utils.c Normal file
View File

@ -0,0 +1,836 @@
/*
* parser_utils.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <piler.h>
#include "trans.h"
#include "ijc.h"
#include "html.h"
void initState(struct _state *state){
int i;
state->message_state = MSG_UNDEF;
state->is_header = 1;
/* by default we are a text/plain message */
state->textplain = 1;
state->texthtml = 0;
state->message_rfc822 = 0;
state->octetstream = 0;
state->base64 = 0;
state->utf8 = 0;
state->qp = 0;
state->htmltag = 0;
state->style = 0;
state->skip_html = 0;
state->n_token = 0;
state->n_body_token = 0;
state->n_chain_token = 0;
state->n_subject_token = 0;
state->content_type_is_set = 0;
state->c_shit = 0;
state->l_shit = 0;
state->line_num = 0;
state->ipcnt = 0;
memset(state->ip, 0, SMALLBUFSIZE);
memset(state->hostname, 0, SMALLBUFSIZE);
memset(state->miscbuf, 0, MAX_TOKEN_LEN);
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
memset(state->from, 0, SMALLBUFSIZE);
memset(state->message_id, 0, SMALLBUFSIZE);
state->urls = NULL;
state->found_our_signo = 0;
state->has_to_dump = 0;
state->fd = -1;
state->num_of_images = 0;
state->num_of_msword = 0;
state->realbinary = 0;
state->boundaries = NULL;
state->n_attachments = 0;
state->has_base64 = 0;
for(i=0; i<MAX_ATTACHMENTS; i++){
state->attachments[i].size = 0;
memset(state->attachments[i].type, 0, SMALLBUFSIZE);
memset(state->attachments[i].filename, 0, SMALLBUFSIZE);
}
memset(state->b_from, 0, SMALLBUFSIZE);
memset(state->b_to, 0, SMALLBUFSIZE);
memset(state->b_subject, 0, MAXBUFSIZE);
memset(state->b_body, 0, BIGBUFSIZE);
}
void freeState(struct _state *state){
freeList(state->urls);
}
int attachment_by_type(struct _state *state, char *type){
int i;
for(i=0; i<MAX_ATTACHMENTS; i++){
if(strstr(state->attachments[i].type, type))
return 1;
}
return 0;
}
/*
* extract bondary
*/
int extract_boundary(char *p, struct _state *state){
char *q;
p += strlen("boundary");
q = strchr(p, '"');
if(q) *q = ' ';
/*
* I've seen an idiot spammer using the following boundary definition in the header:
*
* Content-Type: multipart/alternative;
* boundary=3D"b1_52b92b01a943615aff28b7f4d2f2d69d"
*/
if(strncmp(p, "=3D", 3) == 0){
*(p+3) = '=';
p += 3;
}
p = strchr(p, '=');
if(p){
p++;
for(; *p; p++){
if(isspace(*p) == 0)
break;
}
q = strrchr(p, '"');
if(q) *q = '\0';
q = strrchr(p, '\r');
if(q) *q = '\0';
q = strrchr(p, '\n');
if(q) *q = '\0';
append_boundary(&(state->boundaries), p);
return 1;
}
return 0;
}
int extractNameFromHeaderLine(char *s, char *name, char *resultbuf){
int rc=0;
char buf[SMALLBUFSIZE], *p, *q;
snprintf(buf, SMALLBUFSIZE-1, "%s", s);
p = strstr(buf, name);
if(p){
p += strlen(name);
p = strchr(p, '=');
if(p){
p++;
q = strrchr(p, ';');
if(q) *q = '\0';
q = strrchr(p, '"');
if(q){
*q = '\0';
p = strchr(p, '"');
if(p){
p++;
}
}
snprintf(resultbuf, SMALLBUFSIZE-1, "%s", p);
rc = 1;
}
}
return rc;
}
void fixupEncodedHeaderLine(char *buf){
char *p, *q, *r, *s, u[SMALLBUFSIZE], puf[MAXBUFSIZE];
char *start, *end;
memset(puf, 0, MAXBUFSIZE);
q = buf;
do {
q = split_str(q, " ", u, SMALLBUFSIZE-1);
p = u;
do {
start = strstr(p, "=?");
if(start){
if(start != p){
*start = '\0';
strncat(puf, p, MAXBUFSIZE-1);
*start = '=';
}
/* find the trailing '?=' sequence */
end = strrchr(p, '?'); r = strrchr(p, '=');
if(end && r && r == end+1){
*end = '\0';
p = end + 2;
s = NULL;
if((s = strcasestr(start+2, "?B?"))){
*s = '\0';
decodeBase64(s+3);
}
else if((s = strcasestr(start+2, "?Q?"))){
*s = '\0';
r = s + 3;
for(; *r; r++){
if(*r == '_') *r = ' ';
}
decodeQP(s+3);
}
if(s && strncasecmp(start, "=?utf-8", 5) == 0){
decodeUTF8(s+3);
}
if(s) strncat(puf, s+3, MAXBUFSIZE-1);
}
else {
start = NULL;
}
}
if(!start){
strncat(puf, p, MAXBUFSIZE-1);
}
} while(start);
strncat(puf, " ", MAXBUFSIZE-1);
} while(q);
snprintf(buf, MAXBUFSIZE-1, "%s", puf);
}
void fixupSoftBreakInQuotedPritableLine(char *buf, struct _state *state){
int i=0;
char *p, puf[MAXBUFSIZE];
if(strlen(state->qpbuf) > 0){
memset(puf, 0, MAXBUFSIZE);
strncpy(puf, state->qpbuf, MAXBUFSIZE-1);
strncat(puf, buf, MAXBUFSIZE-1);
memset(buf, 0, MAXBUFSIZE);
memcpy(buf, puf, MAXBUFSIZE);
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
}
if(buf[strlen(buf)-1] == '='){
buf[strlen(buf)-1] = '\0';
i = 1;
}
if(i == 1){
p = strrchr(buf, ' ');
if(p){
memset(state->qpbuf, 0, MAX_TOKEN_LEN);
if(strlen(p) < MAX_TOKEN_LEN-1){
snprintf(state->qpbuf, MAXBUFSIZE-1, "%s", p);
*p = '\0';
}
}
}
}
void fixupBase64EncodedLine(char *buf, struct _state *state){
char *p, puf[MAXBUFSIZE];
if(strlen(state->miscbuf) > 0){
memset(puf, 0, MAXBUFSIZE);
strncpy(puf, state->miscbuf, MAXBUFSIZE-1);
strncat(puf, buf, MAXBUFSIZE-1);
memset(buf, 0, MAXBUFSIZE);
memcpy(buf, puf, MAXBUFSIZE);
memset(state->miscbuf, 0, MAX_TOKEN_LEN);
}
if(buf[strlen(buf)-1] != '\n'){
p = strrchr(buf, ' ');
if(p){
strncpy(state->miscbuf, p+1, MAX_TOKEN_LEN-1);
*p = '\0';
}
}
}
void markHTML(char *buf, struct _state *state){
char *s, puf[MAXBUFSIZE], html[SMALLBUFSIZE];
int k=0, j=0, pos=0;
memset(puf, 0, MAXBUFSIZE);
memset(html, 0, SMALLBUFSIZE);
s = buf;
for(; *s; s++){
if(*s == '<'){
state->htmltag = 1;
puf[k] = ' ';
k++;
memset(html, 0, SMALLBUFSIZE); j=0;
pos = 0;
//printf("start html:%c\n", *s);
}
if(state->htmltag == 1){
if(j == 0 && *s == '!'){
state->skip_html = 1;
//printf("skiphtml=1\n");
}
if(state->skip_html == 0){
if(*s != '>' && *s != '<' && *s != '"'){
//printf("j=%d/%c", j, *s);
html[j] = tolower(*s);
if(j < SMALLBUFSIZE-10) j++;
}
if(isspace(*s)){
if(j > 0){
k += appendHTMLTag(puf, html, pos, state);
memset(html, 0, SMALLBUFSIZE); j=0;
}
pos++;
}
}
}
else {
if(state->style == 0){
puf[k] = *s;
k++;
}
}
if(*s == '>'){
state->htmltag = 0;
state->skip_html = 0;
//printf("skiphtml=0\n");
//printf("end html:%c\n", *s);
//strncat(html, " ", SMALLBUFSIZE-1);
if(j > 0){
strncat(html, " ", SMALLBUFSIZE-1);
k += appendHTMLTag(puf, html, pos, state);
memset(html, 0, SMALLBUFSIZE); j=0;
}
}
}
//printf("append last in line:*%s*, html=+%s+, j=%d\n", puf, html, j);
if(j > 0){ k += appendHTMLTag(puf, html, pos, state); }
strcpy(buf, puf);
}
int appendHTMLTag(char *buf, char *htmlbuf, int pos, struct _state *state){
char *p, html[SMALLBUFSIZE];
int len;
if(pos == 0 && strncmp(htmlbuf, "style ", 6) == 0) state->style = 1;
if(pos == 0 && strncmp(htmlbuf, "/style ", 7) == 0) state->style = 0;
//printf("appendHTML: pos:%d, +%s+\n", pos, htmlbuf);
if(state->style == 1) return 0;
if(strlen(htmlbuf) == 0) return 0;
snprintf(html, SMALLBUFSIZE-1, "HTML*%s", htmlbuf);
len = strlen(html);
if(isSkipHTMLTag(html) == 1) return 0;
if(len > 8 && strchr(html, '=')){
p = strstr(html, "cid:");
if(p){
*(p+3) = '\0';
strncat(html, " ", SMALLBUFSIZE-1);
}
strncat(buf, html, MAXBUFSIZE-1);
return len;
}
if(strstr(html, "http") ){
strncat(buf, html+5, MAXBUFSIZE-1);
return len-5;
}
return 0;
}
int isSkipHTMLTag(char *s){
int i=0;
for(i=0; i<NUM_OF_SKIP_TAGS; i++){
//printf("matching *%s* on *%s*\n", s+5, skip_html_tags[i].entity);
if(strncmp(s+5, skip_html_tags[i].entity, skip_html_tags[i].length) == 0) return 1;
}
return 0;
}
void translateLine(unsigned char *p, struct _state *state){
int url=0;
unsigned char *q=NULL, *P=p;
for(; *p; p++){
/* save position of '=', 2006.01.05, SJ */
if(state->qp == 1 && *p == '='){
q = p;
}
if( (state->message_state == MSG_RECEIVED || state->message_state == MSG_FROM || state->message_state == MSG_TO || state->message_state == MSG_CC) && *p == '@'){ continue; }
if(state->message_state == MSG_SUBJECT && *p == '%'){ continue; }
if(state->message_state == MSG_CONTENT_TYPE && *p == '_' ){ continue; }
if(state->message_state != MSG_BODY && (*p == '.' || *p == '-') ){ continue; }
if(strncasecmp((char *)p, "http://", 7) == 0){ p += 7; url = 1; continue; }
if(strncasecmp((char *)p, "https://", 8) == 0){ p += 8; url = 1; continue; }
if(url == 1 && (*p == '.' || *p == '-' || *p == '_' || *p == '/' || isalnum(*p)) ) continue;
if(url == 1) url = 0;
if(state->texthtml == 1 && state->message_state == MSG_BODY && strncmp((char *)p, "HTML*", 5) == 0){
p += 5;
while(isspace(*p) == 0){
p++;
}
}
if(delimiter_characters[(unsigned int)*p] != ' ' || isalnum(*p) == 0)
*p = ' ';
else {
*p = tolower(*p);
}
}
/* restore the soft break in quoted-printable parts, 2006.01.05, SJ */
if(state->qp == 1 && q && (q > P + strlen((char*)P) - 3))
*q = '=';
}
/*
* reassemble 'V i a g r a' to 'Viagra'
*/
void reassembleToken(char *p){
int i, k=0;
for(i=0; i<strlen(p); i++){
if(isspace(*(p+i-1)) && !isspace(*(p+i)) && isspace(*(p+i+1)) && !isspace(*(p+i+2)) && isspace(*(p+i+3)) && !isspace(*(p+i+4)) && isspace(*(p+i+5)) ){
p[k] = *(p+i); k++;
p[k] = *(p+i+2); k++;
p[k] = *(p+i+4); k++;
i += 5;
}
else {
p[k] = *(p+i);
k++;
}
}
p[k] = '\0';
}
/*
* degenerate a token
*/
void degenerateToken(unsigned char *p){
int i=1, d=0, dp=0;
unsigned char *s;
/* quit if this the string does not end with a punctuation character */
if(!ispunct(*(p+strlen((char *)p)-1)))
return;
s = p;
for(; *p; p++){
if(ispunct(*p)){
d = i;
if(!ispunct(*(p-1)))
dp = d;
}
else
d = dp = i;
i++;
}
*(s+dp) = '\0';
if(*(s+dp-1) == '.' || *(s+dp-1) == '!' || *(s+dp-1) == '?') *(s+dp-1) = '\0';
}
/*
* count the invalid characters (ie. garbage on your display) in the buffer
*/
int countInvalidJunkCharacters(char *p, int replace_junk){
int i=0;
for(; *p; p++){
if(invalid_junk_characters[(unsigned char)*p] == *p){
i++;
if(replace_junk == 1) *p = JUNK_REPLACEMENT_CHAR;
}
}
return i;
}
/*
* detect Chinese, Japan, Korean, ... lines
*/
int countInvalidJunkLines(char *p){
int i=0;
if(*p == '' && *(p+1) == '$' && *(p+2) == 'B'){
for(; *p; p++){
if(*p == '' && *(p+1) == '(' && *(p+2) == 'B')
i++;
}
}
return i;
}
int countNonPrintableCharacters(char *p){
int n = 0;
for(; *p; p++){
if(!isprint(*p) && !isspace(*p)) n++;
}
return n;
}
/*
* is this a hexadecimal numeric string?
*/
int isHexNumber(char *p){
for(; *p; p++){
if(!(*p == '-' || (*p >= 0x30 && *p <= 0x39) || (*p >= 0x41 && *p <= 0x46) || (*p >= 0x61 && *p <= 0x66)) )
return 0;
}
return 1;
}
void fixURL(char *url){
char *p, *q, m[MAX_TOKEN_LEN], fixed_url[MAXBUFSIZE];
int i, dots=0;
struct in_addr addr;
/* chop trailing dot */
if(url[strlen(url)-1] == '.')
url[strlen(url)-1] = '\0';
memset(fixed_url, 0, MAXBUFSIZE);
if((strncasecmp(url, "http://", 7) == 0 || strncasecmp(url, "https://", 8) == 0) ){
p = url;
if(strncasecmp(p, "http://", 7) == 0) p += 7;
if(strncasecmp(p, "https://", 8) == 0) p += 8;
/* skip anything after the host part, 2006.12.11, SJ */
q = strchr(p, '/');
if(q)
*q = '\0';
/*
http://www.ajandekkaracsonyra.hu/email.php?page=email&cmd=unsubscribe&email=yy@xxxx.kom is
chopped to www.ajandekkaracsonyra.hu at this point, 2006.12.15, SJ
*/
dots = countCharacterInBuffer(p, '.');
if(dots < 1)
return;
strncpy(fixed_url, "URL*", MAXBUFSIZE-1);
/* is it a numeric IP-address? */
if(inet_aton(p, &addr)){
addr.s_addr = ntohl(addr.s_addr);
strncat(fixed_url, inet_ntoa(addr), MAXBUFSIZE-1);
strcpy(url, fixed_url);
}
else {
for(i=0; i<=dots; i++){
q = split(p, '.', m, MAX_TOKEN_LEN-1);
if(i>dots-2){
strncat(fixed_url, m, MAXBUFSIZE-1);
if(i < dots)
strncat(fixed_url, ".", MAXBUFSIZE-1);
}
p = q;
}
/* if it does not contain a single dot, the rest of the URL may be
in the next line or it is a fake one, anyway skip, 2006.04.06, SJ
*/
if(countCharacterInBuffer(fixed_url, '.') != 1)
memset(url, 0, MAXBUFSIZE);
else {
for(i=4; i<strlen(fixed_url); i++)
fixed_url[i] = tolower(fixed_url[i]);
strcpy(url, fixed_url);
}
}
}
}
/*
* fix a long FQDN
*/
void fixFQDN(char *fqdn){
char *p, *q, m[MAX_TOKEN_LEN], fixed_fqdn[MAXBUFSIZE];
int i, dots=0;
/* chop trailing dot */
if(fqdn[strlen(fqdn)-1] == '.')
fqdn[strlen(fqdn)-1] = '\0';
memset(fixed_fqdn, 0, MAXBUFSIZE);
p = fqdn;
dots = countCharacterInBuffer(p, '.');
if(dots < 1)
return;
for(i=0; i<=dots; i++){
q = split(p, '.', m, MAX_TOKEN_LEN-1);
if(i>dots-2){
strncat(fixed_fqdn, m, MAXBUFSIZE-1);
if(i < dots)
strncat(fixed_fqdn, ".", MAXBUFSIZE-1);
}
p = q;
}
strcpy(fqdn, fixed_fqdn);
}
/*
* extract the .tld from a name (URL, FQDN, ...)
*/
void getTLDFromName(char *name){
char *p, fixed_name[SMALLBUFSIZE];;
p = strrchr(name, '.');
if(p){
snprintf(fixed_name, SMALLBUFSIZE-1, "URL*%s", p+1);
strcpy(name, fixed_name);
}
}
int isItemOnList(char *item, char *list, char *extralist){
char *p, *q, w[SMALLBUFSIZE], my_list[SMALLBUFSIZE];
if(!item) return 0;
snprintf(my_list, SMALLBUFSIZE-1, "%s,%s", extralist, list);
p = my_list;
do {
p = split(p, ',', w, SMALLBUFSIZE-1);
trimBuffer(w);
if(strlen(w) > 2){
if(w[strlen(w)-1] == '$'){
q = item + strlen(item) - strlen(w) + 1;
if(strncasecmp(q, w, strlen(w)-1) == 0)
return 1;
}
else if(strcasestr(item, w))
return 1;
}
} while(p);
return 0;
}
unsigned long parse_date_header(char *s){
char *p;
unsigned long ts=0;
struct tm tm;
s += 5;
p = s;
if(*p == ' '){ p++; s++; }
p = strchr(s, ',');
if(!p) goto ENDE;
*p = '\0';
if(strcmp(s, "Mon") == 0) tm.tm_wday = 1;
else if(strcmp(s, "Tue") == 0) tm.tm_wday = 2;
else if(strcmp(s, "Wed") == 0) tm.tm_wday = 3;
else if(strcmp(s, "Thu") == 0) tm.tm_wday = 4;
else if(strcmp(s, "Fri") == 0) tm.tm_wday = 5;
else if(strcmp(s, "Sat") == 0) tm.tm_wday = 6;
else if(strcmp(s, "Sun") == 0) tm.tm_wday = 0;
s += 5;
p = strchr(s, ' '); if(!p) goto ENDE;
*p = '\0'; tm.tm_mday = atoi(s); s += 3;
p = strchr(s, ' '); if(!p) goto ENDE;
*p = '\0';
if(strcmp(s, "Jan") == 0) tm.tm_mon = 0;
else if(strcmp(s, "Feb") == 0) tm.tm_mon = 1;
else if(strcmp(s, "Mar") == 0) tm.tm_mon = 2;
else if(strcmp(s, "Apr") == 0) tm.tm_mon = 3;
else if(strcmp(s, "May") == 0) tm.tm_mon = 4;
else if(strcmp(s, "Jun") == 0) tm.tm_mon = 5;
else if(strcmp(s, "Jul") == 0) tm.tm_mon = 6;
else if(strcmp(s, "Aug") == 0) tm.tm_mon = 7;
else if(strcmp(s, "Sep") == 0) tm.tm_mon = 8;
else if(strcmp(s, "Oct") == 0) tm.tm_mon = 9;
else if(strcmp(s, "Nov") == 0) tm.tm_mon = 10;
else if(strcmp(s, "Dec") == 0) tm.tm_mon = 11;
s = p+1;
p = strchr(s, ' '); if(!p) goto ENDE;
tm.tm_year = atoi(s) - 1900; s = p+1;
p = strchr(s, ':'); if(!p) goto ENDE;
*p = '\0'; tm.tm_hour = atoi(s); s = p+1;
p = strchr(s, ':'); if(!p) goto ENDE;
*p = '\0'; tm.tm_min = atoi(s); s = p+1;
p = strchr(s, ' '); if(!p) goto ENDE;
*p = '\0'; tm.tm_sec = atoi(s); s = p+1;
tm.tm_isdst = -1;
ts = mktime(&tm);
ENDE:
return ts;
}

249
src/piler.c Normal file
View File

@ -0,0 +1,249 @@
/*
* piler.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <locale.h>
#include <errno.h>
#include <piler.h>
extern char *optarg;
extern int optind;
int sd;
int nconn = 0;
char *configfile = CONFIG_FILE;
struct __config cfg;
struct __data data;
struct passwd *pwd;
void clean_exit(){
if(sd != -1) close(sd);
freeList(data.blackhole);
syslog(LOG_PRIORITY, "%s has been terminated", PROGNAME);
unlink(cfg.pidfile);
exit(1);
}
void fatal(char *s){
syslog(LOG_PRIORITY, "%s\n", s);
clean_exit();
}
void sigchld(){
int pid, wstat;
while((pid = wait_nohang(&wstat)) > 0){
if(nconn > 0) nconn--;
}
}
void initialiseConfiguration(){
cfg = read_config(configfile);
if(strlen(cfg.username) > 1){
pwd = getpwnam(cfg.username);
if(!pwd) fatal(ERR_NON_EXISTENT_USER);
}
if(getuid() == 0 && pwd){
check_and_create_directories(&cfg, pwd->pw_uid, pwd->pw_gid);
}
if(chdir(cfg.workdir)){
syslog(LOG_PRIORITY, "workdir: *%s*", cfg.workdir);
fatal(ERR_CHDIR);
}
setlocale(LC_MESSAGES, cfg.locale);
setlocale(LC_CTYPE, cfg.locale);
freeList(data.blackhole);
data.blackhole = NULL;
syslog(LOG_PRIORITY, "reloaded config: %s", configfile);
#ifdef HAVE_MEMCACHED
memcached_init(&(data.memc), cfg.memcached_servers, 11211);
#endif
}
int read_key(struct __config *cfg){
int fd, n;
fd = open(KEYFILE, O_RDONLY);
if(fd == -1){
syslog(LOG_PRIORITY, "cannot read keyfile: %s", KEYFILE);
return -1;
}
n = read(fd, cfg->key, KEYLEN);
close(fd);
if(n > 5) return 0;
return 1;
}
int main(int argc, char **argv){
int i, new_sd, yes=1, pid, daemonise=0;
unsigned int clen;
struct sockaddr_in client_addr, serv_addr;
struct in_addr addr;
while((i = getopt(argc, argv, "c:dvVh")) > 0){
switch(i){
case 'c' :
configfile = optarg;
break;
case 'd' :
daemonise = 1;
break;
case 'v' :
case 'V' :
__fatal(PROGNAME " " PROGINFO);
break;
case 'h' :
default :
__fatal("usage: ...");
}
}
(void) openlog(PROGNAME, LOG_PID, LOG_MAIL);
sig_catch(SIGINT, clean_exit);
sig_catch(SIGQUIT, clean_exit);
sig_catch(SIGKILL, clean_exit);
sig_catch(SIGTERM, clean_exit);
sig_catch(SIGHUP, initialiseConfiguration);
data.blackhole = NULL;
sig_block(SIGCHLD);
sig_catch(SIGCHLD, sigchld);
initialiseConfiguration();
if(read_key(&cfg)) fatal(ERR_READING_KEY);
if((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
fatal(ERR_OPEN_SOCKET);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(cfg.listen_port);
inet_aton(cfg.listen_addr, &addr);
serv_addr.sin_addr.s_addr = addr.s_addr;
bzero(&(serv_addr.sin_zero), 8);
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
fatal(ERR_SET_SOCK_OPT);
if(bind(sd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
fatal(ERR_BIND_TO_PORT);
if(listen(sd, cfg.backlog) == -1)
fatal(ERR_LISTEN);
if(drop_privileges(pwd)) fatal(ERR_SETUID);
syslog(LOG_PRIORITY, "%s %s starting", PROGNAME, VERSION);
#if HAVE_DAEMON == 1
if(daemonise == 1) i = daemon(1, 0);
#endif
write_pid_file(cfg.pidfile);
/* main accept loop */
for(;;){
/* let new connections wait if we are too busy now */
if(nconn >= cfg.max_connections) sig_pause();
clen = sizeof(client_addr);
sig_unblock(SIGCHLD);
new_sd = accept(sd, (struct sockaddr *)&client_addr, &clen);
sig_block(SIGCHLD);
if(new_sd == -1) continue;
pid = fork();
if(pid == 0){
sig_uncatch(SIGCHLD);
sig_unblock(SIGCHLD);
sig_uncatch(SIGINT);
sig_uncatch(SIGQUIT);
sig_uncatch(SIGKILL);
sig_uncatch(SIGTERM);
sig_block(SIGHUP);
syslog(LOG_PRIORITY, "connection from client: %s", inet_ntoa(client_addr.sin_addr));
handle_smtp_session(new_sd, &data, &cfg);
_exit(0);
}
else if(pid > 0){
nconn++;
}
else {
syslog(LOG_PRIORITY, "%s", ERR_FORK_FAILED);
}
close(new_sd);
}
return 0;
}

41
src/piler.h Normal file
View File

@ -0,0 +1,41 @@
/*
* piler.h, SJ
*/
#ifndef _PILER_H
#define _PILER_H
#include <misc.h>
#include <list.h>
#include <parser.h>
#include <errmsg.h>
#include <smtpcodes.h>
#include <session.h>
#include <decoder.h>
#include <boundary.h>
#include <defs.h>
#include <sig.h>
#include <av.h>
#include <config.h>
#include <unistd.h>
#ifdef HAVE_MEMCACHED
#include "memc.h"
#endif
int do_av_check(struct session_data *sdata, char *rcpttoemail, char *fromemail, char *virusinfo, struct __data *data, struct __config *cfg);
int make_header_digest(struct session_data *sdata, struct _state *state);
int make_body_digest(struct session_data *sdata);
int processMessage(struct session_data *sdata, struct _state *sstate, struct __config *cfg);
int store_message(struct session_data *sdata, struct _state *state, int stored, struct __config *cfg);
struct __config read_config(char *configfile);
void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid);
void updateCounters(struct session_data *sdata, struct __data *data, struct __counters *counters, struct __config *cfg);
#endif /* _PILER_H */

60
src/pilerconf.c Normal file
View File

@ -0,0 +1,60 @@
/*
* pilerconf.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <piler.h>
extern char *optarg;
extern int optind;
void print_config_all(struct __config *cfg, char *key);
void print_config(char *configfile, struct __config *cfg);
int main(int argc, char **argv){
int i, print_from_file=0;
char *configfile=CONFIG_FILE, *query=NULL;
struct __config cfg;
while((i = getopt(argc, argv, "c:q:nh?")) > 0){
switch(i){
case 'c' :
configfile = optarg;
break;
case 'n' :
print_from_file = 1;
break;
case 'q' :
query = optarg;
break;
case 'h' :
case '?' :
printf("pilerconf [-n -c <configfile>] [-q <key>]\n");
break;
default :
break;
}
}
cfg = read_config(configfile);
if(print_from_file == 1){
print_config(configfile, &cfg);
}
else {
print_config_all(&cfg, query);
}
return 0;
}

499
src/session.c Normal file
View File

@ -0,0 +1,499 @@
/*
* session.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h>
#include <time.h>
#include <piler.h>
void handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){
int i, ret, pos, n, inj=ERR, state, prevlen=0;
char *p, buf[MAXBUFSIZE], puf[MAXBUFSIZE], resp[MAXBUFSIZE], prevbuf[MAXBUFSIZE], last2buf[2*MAXBUFSIZE+1];
char rctptoemail[SMALLBUFSIZE], fromemail[SMALLBUFSIZE], virusinfo[SMALLBUFSIZE], reason[SMALLBUFSIZE];
struct session_data sdata;
struct _state sstate;
int db_conn=0;
int rc;
struct __counters counters;
struct timezone tz;
struct timeval tv1, tv2;
alarm(cfg->session_timeout);
sig_catch(SIGALRM, killChild);
state = SMTP_STATE_INIT;
initSessionData(&sdata);
bzero(&counters, sizeof(counters));
/* open database connection */
db_conn = 0;
#ifdef NEED_MYSQL
rc = 1;
mysql_init(&(sdata.mysql));
mysql_options(&(sdata.mysql), MYSQL_OPT_CONNECT_TIMEOUT, (const char*)&cfg->mysql_connect_timeout);
mysql_options(&(sdata.mysql), MYSQL_OPT_RECONNECT, (const char*)&rc);
if(mysql_real_connect(&(sdata.mysql), cfg->mysqlhost, cfg->mysqluser, cfg->mysqlpwd, cfg->mysqldb, cfg->mysqlport, cfg->mysqlsocket, 0))
db_conn = 1;
else
syslog(LOG_PRIORITY, "%s", ERR_MYSQL_CONNECT);
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: fork()", sdata.ttmpfile);
gettimeofday(&tv1, &tz);
#ifdef HAVE_LMTP
snprintf(buf, MAXBUFSIZE-1, LMTP_RESP_220_BANNER, cfg->hostid);
#else
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_220_BANNER, cfg->hostid);
#endif
send(new_sd, buf, strlen(buf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
while((n = recvtimeout(new_sd, puf, MAXBUFSIZE, TIMEOUT)) > 0){
pos = 0;
/* accept mail data */
if(state == SMTP_STATE_DATA){
/* join the last 2 buffer */
memset(last2buf, 0, 2*MAXBUFSIZE+1);
memcpy(last2buf, prevbuf, MAXBUFSIZE);
memcpy(last2buf+prevlen, puf, MAXBUFSIZE);
if(sdata.hdr_len == 0){
sdata.hdr_len = searchStringInBuffer(last2buf, 2*MAXBUFSIZE+1, "\n\r\n", 3);
if(sdata.hdr_len == 0) searchStringInBuffer(last2buf, 2*MAXBUFSIZE+1, "\n\n", 2);
}
pos = searchStringInBuffer(last2buf, 2*MAXBUFSIZE+1, SMTP_CMD_PERIOD, 5);
if(pos > 0){
/* fix position */
pos = pos - prevlen;
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: period found", sdata.ttmpfile);
/* write data only to (and including) the trailing period (.) */
ret = write(sdata.fd, puf, pos);
sdata.tot_len += ret;
/* fix posistion! */
pos += strlen(SMTP_CMD_PERIOD);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: got: (.), header length=%d", sdata.ttmpfile, sdata.hdr_len);
state = SMTP_STATE_PERIOD;
/* make sure we had a successful read */
rc = fsync(sdata.fd);
close(sdata.fd);
gettimeofday(&tv2, &tz);
sdata.__acquire = tvdiff(tv2, tv1);
if(rc){
syslog(LOG_PRIORITY, "failed writing data: %s", sdata.ttmpfile);
#ifdef HAVE_LMTP
for(i=0; i<sdata.num_of_rcpt_to; i++){
#endif
send(new_sd, SMTP_RESP_421_ERR_WRITE_FAILED, strlen(SMTP_RESP_421_ERR_WRITE_FAILED), 0);
#ifdef HAVE_LMTP
}
#endif
memset(puf, 0, MAXBUFSIZE);
goto AFTER_PERIOD;
}
gettimeofday(&tv1, &tz);
sstate = parseMessage(&sdata, cfg);
gettimeofday(&tv2, &tz);
sdata.__parsed = tvdiff(tv2, tv1);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: parsed message", sdata.ttmpfile);
sdata.need_scan = 1;
#ifdef HAVE_ANTIVIRUS
if(cfg->use_antivirus == 1){
gettimeofday(&tv1, &tz);
sdata.rav = do_av_check(&sdata, rctptoemail, fromemail, &virusinfo[0], data, cfg);
gettimeofday(&tv2, &tz);
sdata.__av = tvdiff(tv2, tv1);
}
#endif
#ifdef HAVE_LMTP
for(i=0; i<sdata.num_of_rcpt_to; i++){
#else
i = 0;
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: round %d in injection", sdata.ttmpfile, i);
extractEmail(sdata.rcptto[i], rctptoemail);
/* copy default config to enable policy support */
//memcpy(&my_cfg, cfg, sizeof(struct __config));
inj = ERR;
if(db_conn == 1){
if(AVIR_VIRUS == sdata.rav){
syslog(LOG_PRIORITY, "%s: found virus: %s", sdata.ttmpfile, virusinfo);
counters.c_virus++;
inj = OK;
} else if(strlen(sdata.bodydigest) < 10) {
syslog(LOG_PRIORITY, "%s: invalid digest", sdata.ttmpfile);
inj = ERR;
} else {
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: processing message", sdata.ttmpfile);
inj = processMessage(&sdata, &sstate, cfg);
}
}
/* set the accept buffer */
snprintf(sdata.acceptbuf, SMALLBUFSIZE-1, "250 Ok %s <%s>\r\n", sdata.ttmpfile, rctptoemail);
if(inj == ERR) snprintf(sdata.acceptbuf, SMALLBUFSIZE-1, "451 %s <%s>\r\n", sdata.ttmpfile, rctptoemail);
send(new_sd, sdata.acceptbuf, strlen(sdata.acceptbuf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, sdata.acceptbuf);
counters.c_rcvd++;
if(inj == ERR_EXISTS){
syslog(LOG_PRIORITY, "%s: discarding duplicate message", sdata.ttmpfile);
counters.c_duplicate++;
}
snprintf(reason, SMALLBUFSIZE-1, "delay=%.2f, delays=%.2f/%.2f/%.2f/%.2f/%.2f/%.2f",
(sdata.__acquire+sdata.__parsed+sdata.__av+sdata.__compress+sdata.__encrypt+sdata.__store)/1000000.0,
sdata.__acquire/1000000.0, sdata.__parsed/1000000.0, sdata.__av/1000000.0, sdata.__compress/1000000.0, sdata.__encrypt/1000000.0, sdata.__store/1000000.0);
syslog(LOG_PRIORITY, "%s: %s got mail size=%d, body digest=%s, %s", sdata.ttmpfile, rctptoemail, sdata.tot_len, sdata.bodydigest, reason);
#ifdef HAVE_LMTP
} /* for */
#endif
unlink(sdata.ttmpfile);
freeState(&sstate);
alarm(cfg->session_timeout);
/* if we have nothing after the trailing (.), we can read
the next command from the network */
if(puf[n-3] == '.' && puf[n-2] == '\r' && puf[n-1] == '\n') continue;
/* if we left something in the puffer, we are ready to proceed
to handle the additional commands, such as QUIT */
/* if we miss the trailing \r\n, ie. we need another read */
if(puf[n-2] != '\r' && puf[n-1] != '\n'){
memmove(puf, puf+pos, n-pos);
memset(puf+n-pos, 0, MAXBUFSIZE-n+pos);
i = recvtimeout(new_sd, buf, MAXBUFSIZE, TIMEOUT);
strncat(puf, buf, MAXBUFSIZE-1-n+pos);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: partial read: %s", sdata.ttmpfile, puf);
pos = 0;
}
} /* PERIOD found */
else {
ret = write(sdata.fd, puf, n);
sdata.tot_len += ret;
memcpy(prevbuf, puf, n);
prevlen = n;
continue;
}
} /* SMTP DATA */
AFTER_PERIOD:
/* handle smtp commands */
memset(resp, 0, MAXBUFSIZE);
p = &puf[pos];
while((p = split_str(p, "\r\n", buf, MAXBUFSIZE-1))){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: got: %s", sdata.ttmpfile, buf);
if(strncasecmp(buf, SMTP_CMD_EHLO, strlen(SMTP_CMD_EHLO)) == 0 || strncasecmp(buf, LMTP_CMD_LHLO, strlen(LMTP_CMD_LHLO)) == 0){
if(state == SMTP_STATE_INIT) state = SMTP_STATE_HELO;
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid);
strncat(resp, buf, MAXBUFSIZE-1);
continue;
/* FIXME: implement the ENHANCEDSTATUSCODE extensions */
}
if(strncasecmp(buf, SMTP_CMD_HELO, strlen(SMTP_CMD_HELO)) == 0){
if(state == SMTP_STATE_INIT) state = SMTP_STATE_HELO;
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
continue;
}
if(strncasecmp(buf, SMTP_CMD_MAIL_FROM, strlen(SMTP_CMD_MAIL_FROM)) == 0){
if(state != SMTP_STATE_HELO){
strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1);
}
else {
state = SMTP_STATE_MAIL_FROM;
snprintf(sdata.mailfrom, SMALLBUFSIZE-1, "%s\r\n", buf);
memset(fromemail, 0, SMALLBUFSIZE);
extractEmail(sdata.mailfrom, fromemail);
strncat(resp, SMTP_RESP_250_OK, strlen(SMTP_RESP_250_OK));
}
continue;
}
if(strncasecmp(buf, SMTP_CMD_RCPT_TO, strlen(SMTP_CMD_RCPT_TO)) == 0){
if(state == SMTP_STATE_MAIL_FROM || state == SMTP_STATE_RCPT_TO){
if(strlen(buf) > SMALLBUFSIZE/2){
strncat(resp, SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO, MAXBUFSIZE-1);
continue;
}
if(sdata.num_of_rcpt_to < MAX_RCPT_TO-1){
snprintf(sdata.rcptto[sdata.num_of_rcpt_to], SMALLBUFSIZE-1, "%s\r\n", buf);
}
state = SMTP_STATE_RCPT_TO;
/* check against blackhole addresses */
extractEmail(buf, rctptoemail);
if(sdata.num_of_rcpt_to < MAX_RCPT_TO-1) sdata.num_of_rcpt_to++;
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
}
else {
strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1);
}
continue;
}
if(strncasecmp(buf, SMTP_CMD_DATA, strlen(SMTP_CMD_DATA)) == 0){
memset(last2buf, 0, 2*MAXBUFSIZE+1);
memset(prevbuf, 0, MAXBUFSIZE);
inj = ERR;
prevlen = 0;
if(state != SMTP_STATE_RCPT_TO){
strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1);
}
else {
sdata.fd = open(sdata.ttmpfile, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
if(sdata.fd == -1){
syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sdata.ttmpfile);
strncat(resp, SMTP_RESP_451_ERR, MAXBUFSIZE-1);
}
else {
state = SMTP_STATE_DATA;
strncat(resp, SMTP_RESP_354_DATA_OK, MAXBUFSIZE-1);
}
}
continue;
}
if(strncasecmp(buf, SMTP_CMD_QUIT, strlen(SMTP_CMD_QUIT)) == 0){
state = SMTP_STATE_FINISHED;
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_221_GOODBYE, cfg->hostid);
send(new_sd, buf, strlen(buf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
unlink(sdata.ttmpfile);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata.ttmpfile);
goto QUITTING;
}
if(strncasecmp(buf, SMTP_CMD_NOOP, strlen(SMTP_CMD_NOOP)) == 0){
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
continue;
}
if(strncasecmp(buf, SMTP_CMD_RESET, strlen(SMTP_CMD_RESET)) == 0){
strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata.ttmpfile);
unlink(sdata.ttmpfile);
initSessionData(&sdata);
state = SMTP_STATE_HELO;
continue;
}
/* by default send 502 command not implemented message */
syslog(LOG_PRIORITY, "%s: invalid command: *%s*", sdata.ttmpfile, buf);
strncat(resp, SMTP_RESP_502_ERR, MAXBUFSIZE-1);
}
/* now we can send our buffered response */
if(strlen(resp) > 0){
send(new_sd, resp, strlen(resp), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, resp);
memset(resp, 0, MAXBUFSIZE);
}
} /* while */
/*
* if we are not in SMTP_STATE_QUIT and the message was not injected,
* ie. we have timed out than send back 421 error message
*/
if(state < SMTP_STATE_QUIT && inj != OK){
snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_421_ERR, cfg->hostid);
send(new_sd, buf, strlen(buf), 0);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf);
goto QUITTING;
}
QUITTING:
updateCounters(&sdata, data, &counters, cfg);
#ifdef NEED_MYSQL
mysql_close(&(sdata.mysql));
#endif
#ifdef HAVE_MEMCACHED
memcached_shutdown(&(data->memc));
#endif
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child has finished");
if(cfg->verbosity >= _LOG_INFO) syslog(LOG_PRIORITY, "processed %llu messages", counters.c_rcvd);
}
void killChild(){
syslog(LOG_PRIORITY, "child is killed by force");
exit(0);
}
void initSessionData(struct session_data *sdata){
int i;
sdata->fd = -1;
create_id(&(sdata->ttmpfile[0]));
unlink(sdata->ttmpfile);
memset(sdata->mailfrom, 0, SMALLBUFSIZE);
snprintf(sdata->client_addr, SMALLBUFSIZE-1, "null");
memset(sdata->whitelist, 0, MAXBUFSIZE);
memset(sdata->blacklist, 0, MAXBUFSIZE);
sdata->hdr_len = 0;
sdata->tot_len = 0;
sdata->num_of_rcpt_to = 0;
sdata->tre = '-';
sdata->rav = AVIR_OK;
sdata->__acquire = sdata->__parsed = sdata->__av = sdata->__store = sdata->__compress = sdata->__encrypt = 0;
for(i=0; i<MAX_RCPT_TO; i++) memset(sdata->rcptto[i], 0, SMALLBUFSIZE);
time(&(sdata->now));
sdata->sent = sdata->now;
}

10
src/session.h Normal file
View File

@ -0,0 +1,10 @@
/*
* session.h, SJ
*/
#include "defs.h"
void handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg);
void initSessionData(struct session_data *sdata);
void killChild();

45
src/sig.c Normal file
View File

@ -0,0 +1,45 @@
/*
* sig.c
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void sig_block(int sig){
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss,sig);
sigprocmask(SIG_BLOCK, &ss, (sigset_t *) 0);
}
void sig_unblock(int sig){
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss,sig);
sigprocmask(SIG_UNBLOCK, &ss, (sigset_t *) 0);
}
void sig_catch(int sig, void (*f)()){
struct sigaction sa;
sa.sa_handler = f;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(sig, &sa, (struct sigaction *) 0);
}
void sig_uncatch(int sig){
void (*sig_defaulthandler)() = SIG_DFL;
sig_catch(sig, sig_defaulthandler);
}
void sig_pause(){
sigset_t ss;
sigemptyset(&ss);
sigsuspend(&ss);
}
int wait_nohang(int *wstat){
return waitpid(-1, wstat, WNOHANG);
}

15
src/sig.h Normal file
View File

@ -0,0 +1,15 @@
/*
* sig.h, SJ
*/
#ifndef _SIG_H
#define _SIG_H
void sig_block(int sig);
void sig_unblock(int sig);
void sig_catch(int sig, void (*f)());
void sig_uncatch(int sig);
void sig_pause();
int wait_nohang(int *wstat);
#endif

61
src/smtpcodes.h Normal file
View File

@ -0,0 +1,61 @@
/*
* smtpcodes.h, SJ
*/
// SMTP states
#define SMTP_STATE_INIT 0
#define SMTP_STATE_HELO 1
#define SMTP_STATE_MAIL_FROM 2
#define SMTP_STATE_RCPT_TO 3
#define SMTP_STATE_DATA 4
#define SMTP_STATE_PERIOD 5
#define SMTP_STATE_QUIT 6
#define SMTP_STATE_FINISHED 7
// SMTP commands
#define SMTP_CMD_HELO "HELO"
#define SMTP_CMD_EHLO "EHLO"
#define SMTP_CMD_MAIL_FROM "MAIL FROM:"
#define SMTP_CMD_RCPT_TO "RCPT TO:"
#define SMTP_CMD_DATA "DATA"
#define SMTP_CMD_PERIOD "\x0d\x0a\x2e\x0d\x0a"
#define SMTP_CMD_QUIT "QUIT"
#define SMTP_CMD_RESET "RSET"
#define SMTP_CMD_NOOP "NOOP"
#define SMTP_CMD_XFORWARD "XFORWARD"
#define SMTP_CMD_XCLIENT "XCLIENT"
// SMTP responses
#define SMTP_RESP_220_BANNER "220 %s ESMTP\r\n"
#define SMTP_RESP_221_GOODBYE "221 %s Goodbye\r\n"
#define SMTP_RESP_250_OK "250 Ok\r\n"
#define SMTP_RESP_250_EXTENSIONS "250-%s\r\n250-PIPELINING\r\n250-SIZE\r\n250 8BITMIME\r\n"
#define SMTP_RESP_354_DATA_OK "354 Send mail data; end it with <CRLF>.<CRLF>\r\n"
#define SMTP_RESP_421_ERR "421 %s Error: timed out\r\n"
#define SMTP_RESP_421_ERR_TMP "421 %s service not available\r\n"
#define SMTP_RESP_421_ERR_WRITE_FAILED "421 writing queue file failed\r\n"
#define SMTP_RESP_450_ERR_CMD_NOT_IMPLEMENTED "450 command not implemented\r\n"
#define SMTP_RESP_451_ERR "451 Error in processing, try again later\r\n"
#define SMTP_RESP_502_ERR "502 Command not implemented\r\n"
#define SMTP_RESP_503_ERR "503 Bad command sequence\r\n"
#define SMTP_RESP_550_ERR "550 Access denied.\r\n"
#define SMTP_RESP_550_ERR_PREF "550 Access denied."
#define SMTP_RESP_550_INVALID_RECIPIENT "550 Unknown recipient\r\n"
#define SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO "550 too long recipient\r\n"
#define SMTP_RESP_552_ERR "552 Too many recipients\r\n"
// LMTP commands
#define LMTP_CMD_LHLO "LHLO"
// LMTP responses
#define LMTP_RESP_220_BANNER "220 %s LMTP\r\n"

134
src/store.c Normal file
View File

@ -0,0 +1,134 @@
/*
* store.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <piler.h>
#include <zlib.h>
#include <openssl/blowfish.h>
#include <openssl/evp.h>
int store_message(struct session_data *sdata, struct _state *state, int stored, struct __config *cfg){
int ret=0, rc, fd, n, len=sdata->tot_len;
char *addr, *p0, *p1, *p2, s[SMALLBUFSIZE];
struct stat st;
Bytef *z=NULL;
uLongf dstlen;
EVP_CIPHER_CTX ctx;
unsigned char *outbuf=NULL;
int outlen, tmplen;
struct timezone tz;
struct timeval tv1, tv2;
/* fix data length to store */
/*if(stored == 1 && sdata->hdr_len > 100){
len = sdata->hdr_len;
}*/
fd = open(sdata->ttmpfile, O_RDONLY);
if(fd == -1) return ret;
gettimeofday(&tv1, &tz);
addr = mmap(NULL, sdata->tot_len, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
if(addr == MAP_FAILED) return ret;
dstlen = compressBound(len);
z = malloc(dstlen);
if(z == NULL){
munmap(addr, sdata->tot_len);
return ret;
}
rc = compress(z, &dstlen, (const Bytef *)addr, len);
gettimeofday(&tv2, &tz);
sdata->__compress += tvdiff(tv2, tv1);
munmap(addr, sdata->tot_len);
if(rc != Z_OK) goto ENDE;
gettimeofday(&tv1, &tz);
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, cfg->key, cfg->iv);
outbuf = malloc(dstlen + EVP_MAX_BLOCK_LENGTH);
if(outbuf == NULL) goto ENDE;
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, z, dstlen)) goto ENDE;
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen)) goto ENDE;
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
gettimeofday(&tv2, &tz);
sdata->__encrypt += tvdiff(tv2, tv1);
/* create a filename according to piler_id */
snprintf(s, sizeof(s)-1, "%s/%c%c/%c%c/%c%c/%s", cfg->queuedir, sdata->ttmpfile[RND_STR_LEN-6], sdata->ttmpfile[RND_STR_LEN-5], sdata->ttmpfile[RND_STR_LEN-4], sdata->ttmpfile[RND_STR_LEN-3], sdata->ttmpfile[RND_STR_LEN-2], sdata->ttmpfile[RND_STR_LEN-1], sdata->ttmpfile);
p0 = strrchr(s, '/'); if(!p0) goto ENDE;
*p0 = '\0';
if(stat(s, &st)){
p1 = strrchr(s, '/'); if(!p1) goto ENDE;
*p1 = '\0';
p2 = strrchr(s, '/'); if(!p2) goto ENDE;
*p2 = '\0';
mkdir(s, 0755);
*p2 = '/';
mkdir(s, 0755);
*p1 = '/';
mkdir(s, 0755);
}
*p0 = '/';
fd = open(s, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
if(fd == -1) goto ENDE;
n = write(fd, outbuf, outlen);
if(n == outlen){
ret = 1;
}
fsync(fd);
close(fd);
if(ret == 0){
unlink(s);
}
ENDE:
if(outbuf) free(outbuf);
if(z) free(z);
return ret;
}

69
src/tai.c Normal file
View File

@ -0,0 +1,69 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "tai.h"
static char hex[16] = "0123456789abcdef";
void tai_pack(char *s, struct tai *t){
unsigned long long x;
x = t->x;
s[7] = x & 255; x >>= 8;
s[6] = x & 255; x >>= 8;
s[5] = x & 255; x >>= 8;
s[4] = x & 255; x >>= 8;
s[3] = x & 255; x >>= 8;
s[2] = x & 255; x >>= 8;
s[1] = x & 255; x >>= 8;
s[0] = x;
}
void taia_pack(char *s, struct taia *t){
unsigned long x;
tai_pack(s, &t->sec);
s += 8;
x = t->atto;
s[7] = x & 255; x >>= 8;
s[6] = x & 255; x >>= 8;
s[5] = x & 255; x >>= 8;
s[4] = x;
x = t->nano;
s[3] = x & 255; x >>= 8;
s[2] = x & 255; x >>= 8;
s[1] = x & 255; x >>= 8;
s[0] = x;
}
void taia_now(struct taia *t){
struct timeval now;
gettimeofday(&now,(struct timezone *) 0);
t->sec.x = 4611686018427387914ULL + (uint64) now.tv_sec;
t->nano = 1000 * now.tv_usec + 500;
t->atto = 0;
}
void tai_timestamp(char *s){
struct tai now;
char nowpack[TAI_PACK];
int i;
now.x = 4611686018427387914ULL + (unsigned long long) time((long *) 0);
tai_pack(nowpack, &now);
for (i = 0;i < 8;++i) {
*(s+i*2) = hex[(nowpack[i] >> 4) & 15];
*(s+i*2+1) = hex[nowpack[i] & 15];
}
*(s+2*TAI_PACK) = '\0';
}

25
src/tai.h Normal file
View File

@ -0,0 +1,25 @@
/*
* tai.h, SJ
*/
#define TAI_PACK 8
#define TAIA_PACK 16
#define TIMESTAMP 25
typedef unsigned long long uint64;
struct tai {
uint64 x;
};
struct taia {
struct tai sec;
unsigned long nano; /* 0...999999999 */
unsigned long atto; /* 0...999999999 */
};
void taia_now(struct taia *t);
void taia_pack(char *s, struct taia *t);

70
src/test.c Normal file
View File

@ -0,0 +1,70 @@
/*
* test.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <locale.h>
#include <piler.h>
int main(int argc, char **argv){
int i, rc;
struct timezone tz;
struct timeval tv_spam_start, tv_spam_stop;
struct session_data sdata;
struct _state state;
struct __config cfg;
struct stat st;
if(argc < 2){
fprintf(stderr, "usage: %s <message>\n", argv[0]);
exit(1);
}
if(stat(argv[1], &st) != 0){
fprintf(stderr, "%s is not found\n", argv[1]);
return 0;
}
cfg = read_config(CONFIG_FILE);
printf("locale: %s\n", setlocale(LC_MESSAGES, cfg.locale));
setlocale(LC_CTYPE, cfg.locale);
rc = 0;
sdata.num_of_rcpt_to = -1;
time(&(sdata.now));
sdata.sent = 0;
memset(sdata.rcptto[0], 0, SMALLBUFSIZE);
snprintf(sdata.ttmpfile, SMALLBUFSIZE-1, "%s", argv[1]);
state = parseMessage(&sdata, &cfg);
printf("from: %s\n", state.b_from);
printf("to: %s\n", state.b_to);
printf("subject: %s\n", state.b_subject);
printf("message-id: %s\n", state.message_id);
printf("body: %s\n", state.b_body);
printf("body digest: %s\n", sdata.bodydigest);
for(i=0; i<state.n_attachments; i++){
printf("i:%d, name=*%s*, type: *%s*, size: %d\n", i, state.attachments[i].filename, state.attachments[i].type, state.attachments[i].size);
}
gettimeofday(&tv_spam_start, &tz);
freeState(&state);
gettimeofday(&tv_spam_stop, &tz);
return 0;
}

26
src/trans.h Normal file
View File

@ -0,0 +1,26 @@
/*
* trans.h, SJ
*/
static char delimiter_characters[] = {
'x','x','x','x', 'x','x','x','x', 'x','x','x','x', 'x','x','x','x',
'x','x','x','x', 'x','x','x','x', 'x','x','x','x', 'x','x','x','x',
'x',' ','"','#', ' ','%','&','x', '(',')','*','+', ',',' ',' ','/',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',':',';', '<','=','>','?',
'@',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ','[', '\\',']','^','_',
'`',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ','{', '|','}','~','x',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ',
' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' ', ' ',' ',' ',' '
};