/*
* Copyright (c) 2005 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/queue.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#include <netinet/in.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <getopt.h>
#include <err.h>
#include <sha1.h>
#include <event.h>
#include <evdns.h>
#include "util.h"
#include "smtp.h"
#include "smtp_messages.h"
#include "honeyd_overload.h"
extern int debug;
#define DFPRINTF(x, y) do { \
if (debug >= x) fprintf y; \
} while (0)
ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
/* globals */
FILE *flog_email = NULL; /* log the email transactions somewhere */
const char *log_datadir = NULL; /* log the data somewhere */
static char datadir_buf[1024];
static char getcwdbuf[1024];
static char *domains[] = {
"iridic", "bocoy", "hers", "alfa", "chital", "sound", "razz",
"update", "gown", "teeter", "embark", "valeta", "sipid", "whally",
"dewcup", "shabby", "eral", "kibble", "samh", "artha", "zither",
"bench", "duffel", "census", "hacker", "booger", "hobbil", "apish",
"arris", "thyme", "stays", "begut", "unhid", "subgod", "genal",
"fluty", "gossy", "skiver", "secque", "fetish", "osse", "dipyre",
"germin", "datary", "muffle", "refuse", "semis", "vireo", "riser",
"panada", "rackle", "dhyana", "crena", "upcall", "cumbu", "pinta",
"finial", "euphon", "auxin", "voiced"
};
static char *hosts[] = {
"neofetal", "theonomy", "panicked", "securely", "palgat", "rejoice",
"teagle", "unkeyed", "calor", "overpick", "runefolk", "trend",
"nunship", "leveling", "messe", "baetuli", "bossing", "mystic",
"cnida", "premove", "brassily", "fossiled", "fibril", "marooner",
"pataka", "bailee", "futurism", "tropate", "stuffer", "boost",
"portitor", "tussah", "goatskin", "clition", "antiwit", "scind",
"ruggedly", "chummer", "sloan", "mescal", "redub", "cozily",
"drawout", "matin", "acetated", "mustang", "shuck", "bruscus",
"yummy", "swiney", "snubby", "handrail", "centimo", "wind", "dog",
"magic", "wonder"
};
int
smtp_set_datadir(const char *optarg)
{
struct stat sb;
getcwd(getcwdbuf, sizeof(getcwdbuf));
if (*optarg != '/') {
snprintf(datadir_buf, sizeof(datadir_buf),
"%s/%s", getcwdbuf, optarg);
log_datadir = datadir_buf;
} else {
log_datadir = optarg;
}
if (stat(log_datadir, &sb) == -1 || (sb.st_mode & S_IFDIR) == 0)
return (-1);
return (0);
}
static char *
random_hostname(void)
{
static char hostname[128];
int domainindex, hostindex;
domainindex = rand() % (sizeof(domains)/sizeof(char *));
hostindex = rand() % (sizeof(hosts)/sizeof(char *));
snprintf(hostname, sizeof(hostname), "%s.%s.com",
hosts[hostindex], domains[domainindex]);
return hostname;
}
#define ROL64(x, b) (((x) << b) | ((x) >> (64 - b)))
#define ROR64(x, b) (((x) >> b) | ((x) << (64 - b)))
/*
* Thomas Wang's 64-bit hash function from
* www.concentric.net/~Ttwang/tech/inthash.htm
*/
static __inline uint64_t
longhash1(uint64_t key)
{
key += ~(key << 32);
key ^= ROR64(key, 22);
key += ~(key << 13);
key ^= ROR64(key, 8);
key += (key << 3);
key ^= ROR64(key, 15);
key += ~(key << 27);
key ^= ROR64(key, 31);
return key;
}
/* Generic SMTP related code */
char *
smtp_logline(struct smtp_ta *ta)
{
static char line[1024];
struct keyvalue *entry;
char *srcipaddress = kv_find(&ta->dictionary, "$srcipaddress");
char *srcname = kv_find(&ta->dictionary, "$srcname");
char *sender = kv_find(&ta->dictionary, "$sender");
snprintf(line, sizeof(line), "%ld %s[%s]: %s ->",
time(NULL), srcipaddress, srcname, sender);
TAILQ_FOREACH(entry, &ta->dictionary, next) {
if (strcmp(entry->key, "$recipient"))
continue;
strlcat(line, " ", sizeof(line));
strlcat(line, entry->value, sizeof(line));
}
return (line);
}
void
smtp_clear_state(struct smtp_ta *ta)
{
ta->state = EXPECT_HELO;
kv_remove(&ta->dictionary, "$sender");
kv_remove(&ta->dictionary, "$srcname");
kv_remove(&ta->dictionary, "$realuser");
kv_remove(&ta->dictionary, "$vrfyuser");
/* Remove all recipients */
while (kv_remove(&ta->dictionary, "$recipient"))
;
}
/* Callbacks for SMTP handling */
char *
smtp_response(struct smtp_ta *ta, struct keyvalue data[]) {
static char line[1024];
struct keyvalue *cur;
for (cur = &data[0]; cur->key != NULL; cur++) {
if (strcmp(ta->mailer_id, cur->key) == 0)
break;
}
if (cur->key == NULL)
return (NULL);
strlcpy(line, cur->value, sizeof(line));
TAILQ_FOREACH(cur, &ta->dictionary, next) {
strrpl(line, sizeof(line), cur->key, cur->value);
}
return (line);
}
void
smtp_handle_helo_cb(int result, char type, int count, int ttl,
void *addresses, void *arg)
{
struct smtp_ta *ta = arg;
char *response;
if (ta->dns_canceled) {
smtp_ta_free(ta);
return;
}
ta->dns_pending = 0;
if (result == DNS_ERR_NONE && count == 1) {
char *hostname = *(char **)addresses;
kv_replace(&ta->dictionary, "$srcname", hostname);
}
response = smtp_response(ta, helo);
bufferevent_write(ta->bev, response, strlen(response));
ta->state = EXPECT_MAILFROM;
}
int
smtp_handle_helo(struct smtp_ta *ta, char *line)
{
struct sockaddr_in *sin = (struct sockaddr_in *)&ta->sa;
char *domainname;
if (line == NULL) {
char *response = smtp_response(ta, heloerror);
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
smtp_clear_state(ta);
domainname = strsep(&line, " ");
kv_replace(&ta->dictionary, "$srcname", domainname);
evdns_resolve_reverse(&sin->sin_addr, 0, smtp_handle_helo_cb, ta);
ta->dns_pending = 1;
return (0);
}
int
smtp_handle_ehlo(struct smtp_ta *ta, char *line)
{
char *domainname, *response;
if (line == NULL) {
response = smtp_response(ta, ehloerror);
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
smtp_clear_state(ta);
domainname = strsep(&line, " ");
kv_replace(&ta->dictionary, "$srcname", domainname);
response = smtp_response(ta, ehlo);
bufferevent_write(ta->bev, response, strlen(response));
ta->state = EXPECT_MAILFROM;
return (0);
}
int
smtp_handle_mailfrom(struct smtp_ta *ta, char *line)
{
char *response;
char *from = strsep(&line, " ");
if (kv_find(&ta->dictionary, "$sender")) {
response = smtp_response(ta, mailfromerror);
} else {
kv_add(&ta->dictionary, "$sender", from);
response = smtp_response(ta, mailfrom);
ta->state = EXPECT_RCPT;
}
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
int
smtp_handle_rcpt(struct smtp_ta *ta, char *line)
{
char *response;
char *from = strsep(&line, " ");
if (kv_find(&ta->dictionary, "$sender") == NULL) {
response = smtp_response(ta, rcpttoerror);
} else {
kv_add(&ta->dictionary, "$recipient", from);
response = smtp_response(ta, rcptto);
}
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
int
smtp_handle_data(struct smtp_ta *ta, char *line)
{
char *response;
if (kv_find(&ta->dictionary, "$sender") == NULL) {
response = smtp_response(ta, datanomail);
} else if (kv_find(&ta->dictionary, "$recipient") == NULL) {
response = smtp_response(ta, datanorcpt);
} else {
response = smtp_response(ta, data);
ta->state = EXPECT_DATA;
}
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
int
smtp_handle_quit(struct smtp_ta *ta, char *line)
{
char *response;
response = smtp_response(ta, quit);
bufferevent_write(ta->bev, response, strlen(response));
ta->wantclose = 1;
return (0);
}
int
smtp_handle_help(struct smtp_ta *ta, char *line)
{
char *response;
if ( line != NULL ) {
kv_replace(&ta->dictionary, "$helpask", line);
response = smtp_response(ta, helperror);
} else {
response = smtp_response(ta, help);
}
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
int
smtp_handle_noop(struct smtp_ta *ta, char *line)
{
char *response;
response = smtp_response(ta, noop);
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
int
smtp_handle_rset(struct smtp_ta *ta, char *line)
{
char *response;
response = smtp_response(ta, rset);
bufferevent_write(ta->bev, response, strlen(response));
smtp_clear_state(ta);
return (0);
}
int
smtp_handle_vrfy(struct smtp_ta *ta, char *line)
{
char *response;
if (line == NULL || strlen(line) == 0) {
response = smtp_response(ta, vrfyerror);
} else {
char *where = strchr(line, '@');
if (where && strchr(where + 1, '.') != NULL) {
/* This does not strip the braces, etc. */
kv_replace(&ta->dictionary, "$realuser", line);
response = smtp_response(ta, vrfy);
} else {
kv_replace(&ta->dictionary, "$vrfyuser", line);
response = smtp_response(ta, vrfynouser);
}
}
bufferevent_write(ta->bev, response, strlen(response));
return (0);
}
int
smtp_handle_dot(struct smtp_ta *ta)
{
char *response;
if (strcmp(ta->mailer_id, "sendmail") == 0) {
char *qchar = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
char qspec[7], qnr[7+5+1];
struct tm tm;
time_t now = time(NULL);
localtime_r(&now, &tm);
qspec[0] = qchar[tm.tm_year % 60];
qspec[1] = qchar[tm.tm_mon];
qspec[2] = qchar[tm.tm_mday];
qspec[3] = qchar[tm.tm_hour];
qspec[4] = qchar[tm.tm_min];
qspec[5] = qchar[tm.tm_sec];
qspec[6] = '\0';
snprintf(qnr, sizeof(qnr), "%s%05d", qspec, rand() % 100000);
kv_replace(&ta->dictionary, "$queuenr", qnr);
} else {
char qnr[12];
snprintf(qnr, sizeof(qnr), "%04X%06X",
rand() % 65536, rand() % (65536*256));
kv_replace(&ta->dictionary, "$queuenr", qnr);
}
response = smtp_response(ta, dot);
bufferevent_write(ta->bev, response, strlen(response));
if (flog_email != NULL) {
fprintf(flog_email, "%s\n", smtp_logline(ta));
fflush(flog_email);
}
if (log_datadir != NULL) {
smtp_store(ta, log_datadir);
}
smtp_clear_state(ta);
return (0);
}
int
smtp_handle(struct smtp_ta *ta, char *line)
{
char *command;
if (ta->state == EXPECT_DATA) {
/* Wait for the single dot */
if (strcmp(line, ".") == 0) {
smtp_handle_dot(ta);
} else {
kv_add(&ta->dictionary, "data", line);
}
return (0);
}
kv_replace(&ta->dictionary, "$cmd", line);
command = strsep(&line, " ");
/* Special case the commands with space in them */
if (strcasecmp(command, "mail") == 0 ||
strcasecmp(command, "rcpt") == 0) {
char *next = strsep(&line, " ");
if (next != NULL)
command[strlen(command)] = ' ';
}
if (strcasecmp(command, "HELO") == 0) {
return (smtp_handle_helo(ta, line));
} else if (strcasecmp(command, "EHLO") == 0) {
return (smtp_handle_ehlo(ta, line));
} else if (strcasecmp(command, "MAIL FROM:") == 0 &&
line != NULL && strlen(line)) {
return (smtp_handle_mailfrom(ta, line));
} else if (strcasecmp(command, "RCPT TO:") == 0 &&
line != NULL && strlen(line)) {
return (smtp_handle_rcpt(ta, line));
} else if (strcasecmp(command, "DATA") == 0) {
return (smtp_handle_data(ta, line));
} else if (strcasecmp(command, "HELP") == 0) {
return (smtp_handle_help(ta, line));
} else if (strcasecmp(command, "QUIT") == 0) {
return (smtp_handle_quit(ta, line));
} else if (strcasecmp(command, "NOOP") == 0) {
return (smtp_handle_noop(ta, line));
} else if (strcasecmp(command, "RSET") == 0) {
return (smtp_handle_rset(ta, line));
} else if (strcasecmp(command, "VRFY") == 0) {
return (smtp_handle_vrfy(ta, line));
} else {
char *response = smtp_response(ta, errors);
bufferevent_write(ta->bev, response, strlen(response));
}
return (0);
}
int
smtp_lock(const char *lockfile)
{
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
int fd = open(lockfile, O_CREAT|O_RDWR, mode);
if (fd == -1) {
warn("%s: open(%s)", __func__, lockfile);
return (-1);
}
if (flock(fd, LOCK_EX) == -1) {
warn("%s: flock", __func__);
close(fd);
return (-1);
}
return (fd);
}
void
smtp_unlock(int fd)
{
if (flock(fd, LOCK_UN) == -1)
warn("%s: flock", __func__);
close(fd);
}
int
smtp_chmkdir_one(const char *component)
{
mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IXGRP|S_IRGRP;
struct stat sb;
if (stat(component, &sb) == -1) {
if (mkdir(component, mode) == -1) {
warn("%s: %s: mkdir(%s)",
__func__,
getcwd(getcwdbuf, sizeof(getcwdbuf)), component);
return (-1);
}
} else if ((sb.st_mode & S_IFDIR) == 0) {
warnx("%s: something in the way of directory: %s.",
__func__, component);
return (-1);
}
if (chdir(component) == -1) {
warn("%s: %s: chdir(%s)",
__func__, getcwd(getcwdbuf, sizeof(getcwdbuf)), component);
return (-1);
}
return (0);
}
int
smtp_chmkdir(const char *datadir, const char *address)
{
char buf[32], *myaddress = buf, *p;
strlcpy(buf, address, sizeof(buf));
if (chdir(datadir) == -1) {
warn("%s: chdir(%s)", __func__, datadir);
return (-1);
}
while ((p = strsep(&myaddress, ".")) != NULL) {
if (smtp_chmkdir_one(p) == -1)
return (-1);
}
return (0);
}
/* Assumes a lock on the directory */
int
smtp_get_count(const char *countname)
{
int fd = open(countname, O_RDONLY, 0);
int count = -1;
if (fd == -1) {
if (errno == ENOENT)
return (0);
warn("%s: open(%s)", __func__, countname);
return (-1);
}
if (atomicio(read, fd, &count, sizeof(count)) != count)
goto out;
/* We are good */
out:
close(fd);
return (count);
}
int
smtp_write_count(const char *countname, int count)
{
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
int fd = open(countname, O_CREAT|O_TRUNC|O_WRONLY, mode);
if (fd == -1) {
warn("%s: open(%s)", __func__, countname);
return (-1);
}
if (atomicio(write, fd, &count, sizeof(count)) != count) {
count = -1;
goto out;
}
/* We are good */
out:
close(fd);
return (count);
}
#define TOHEX(x) ((x) < 10 ? (x) + '0' : (x) - 10 + 'a')
char *
smtp_hashed_store(const char *datadir, void *data, size_t datalen)
{
SHA1_CTX ctx;
u_char digest[SHA1_DIGESTSIZE];
static char adigest[2*SHA1_DIGESTSIZE+1];
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
struct stat sb;
int fd, i;
SHA1Init(&ctx);
SHA1Update(&ctx, data, datalen);
SHA1Final(digest, &ctx);
for (i = 0; i < sizeof(digest); ++i) {
adigest[i*2] = TOHEX(digest[i] & 0xf);
adigest[i*2 + 1] = TOHEX(digest[i] >> 4);
}
adigest[2*SHA1_DIGESTSIZE] = '\0';
if (chdir(datadir) == -1) {
warn("%s: chdir(%s)", __func__, datadir);
return (adigest);
}
for (i = 0; i < 3; ++i) {
char component[4];
memcpy(component + 1, adigest + 2*i, 2);
component[0] = '.';
component[3] = '\0';
if (smtp_chmkdir_one(component) == -1)
return (adigest);
}
/* Somebody beat us to it */
if (stat(adigest, &sb) != -1)
return (adigest);
if ((fd = open(adigest, O_CREAT|O_TRUNC|O_WRONLY, mode)) == -1) {
warn("%s: open(%s)", __func__, adigest);
return (adigest);
}
atomicio(write, fd, data, datalen);
close(fd);
return (adigest);
}
int
smtp_write_email(struct smtp_ta *ta, int count)
{
struct evbuffer *buffer;
char countname[10], *data;
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
int fd;
int res = -1;
snprintf(countname, sizeof(countname), "%d", count);
if ((fd = open(countname, O_CREAT|O_TRUNC|O_WRONLY, mode)) == -1) {
warn("%s: open(%s)", __func__, countname);
return (-1);
}
if ((buffer = evbuffer_new()) == NULL) {
warn("%s: evbuffer_new", __func__);
goto out;
} else {
char *hash;
struct keyvalue *entry;
char *srcname = kv_find(&ta->dictionary, "$srcname");
char *sender = kv_find(&ta->dictionary, "$sender");
evbuffer_add_printf(buffer, "srcname: %s\n", srcname);
evbuffer_add_printf(buffer, "sender: %s\n", sender);
TAILQ_FOREACH(entry, &ta->dictionary, next) {
if (strcmp(entry->key, "$recipient"))
continue;
evbuffer_add_printf(buffer,
"recipient: %s\n", entry->value);
}
/* Find the headers and log them */
while ((data = kv_find(&ta->dictionary, "data")) != NULL) {
int done = 0;
if (strlen(data))
evbuffer_add_printf(buffer, "%s\n", data);
else
done = 1;
kv_remove(&ta->dictionary, "data");
if (done)
break;
}
evbuffer_write(buffer, fd);
/*
* Log the rest of the data to the buffer, but we are going
* to treat it differently.
*/
while ((data = kv_find(&ta->dictionary, "data")) != NULL) {
evbuffer_add_printf(buffer, "%s\n", data);
kv_remove(&ta->dictionary, "data");
}
hash = smtp_hashed_store(log_datadir,
EVBUFFER_DATA(buffer), EVBUFFER_LENGTH(buffer));
evbuffer_drain(buffer, -1);
evbuffer_add_printf(buffer, "\n%s\n", hash);
evbuffer_write(buffer, fd);
}
res = 0;
out:
if (buffer != NULL)
evbuffer_free(buffer);
close(fd);
return (res);
}
void
smtp_store(struct smtp_ta *ta, const char *dir)
{
int lock_fd, count;
char component[10];
/* Do something here */
char *srcip = kv_find(&ta->dictionary, "$srcipaddress");
/* Create the directory we need. */
if (smtp_chmkdir(log_datadir, srcip) == -1)
return;
if ((lock_fd = smtp_lock(LOCKNAME)) == -1)
return;
/* Do the maintenance while we are in the same directory */
count = smtp_get_count(COUNTNAME);
smtp_write_count(COUNTNAME, count + 1);
snprintf(component, sizeof(component), "%d", count / 256);
if (smtp_chmkdir_one(component) == -1)
goto out;
smtp_write_email(ta, count);
out:
smtp_unlock(lock_fd);
}
char *
smtp_readline(struct bufferevent *bev)
{
struct evbuffer *buffer = EVBUFFER_INPUT(bev);
char *data = EVBUFFER_DATA(buffer);
size_t len = EVBUFFER_LENGTH(buffer);
char *line;
int i;
for (i = 0; i < len; i++) {
if (data[i] == '\r' || data[i] == '\n')
break;
}
if (i == len)
return (NULL);
if ((line = malloc(i + 1)) == NULL) {
fprintf(stderr, "%s: out of memory\n", __func__);
evbuffer_drain(buffer, i);
return (NULL);
}
memcpy(line, data, i);
line[i] = '\0';
if ( i < len - 1 ) {
char fch = data[i], sch = data[i+1];
/* Drain one more character if needed */
if ( (sch == '\r' || sch == '\n') && sch != fch )
i += 1;
}
evbuffer_drain(buffer, i + 1);
return (line);
}
void
smtp_readcb(struct bufferevent *bev, void *arg)
{
char *line;
while ((line = smtp_readline(bev)) != NULL) {
struct smtp_ta *ta = arg;
int res;
res = smtp_handle(ta, line);
DFPRINTF(1, (stderr, "%s: %s\n",
kv_find(&ta->dictionary, "$srcipaddress"),
line));
free(line);
/* Destroy the state machine on error */
if (res == -1) {
smtp_ta_free(ta);
return;
}
}
}
void
smtp_writecb(struct bufferevent *bev, void *arg)
{
struct smtp_ta *ta = arg;
if (ta->wantclose)
smtp_ta_free(ta);
}
void
smtp_errorcb(struct bufferevent *bev, short what, void *arg)
{
fprintf(stderr, "%s: called with %p, freeing\n", __func__, arg);
smtp_ta_free(arg);
}
/* Tear down a connection */
void
smtp_ta_free(struct smtp_ta *ta)
{
struct keyvalue *entry;
if (ta->dns_pending && !ta->dns_canceled) {
/* if we have a pending dns lookup, tell it to cancel */
ta->dns_canceled = 1;
return;
}
while ((entry = TAILQ_FIRST(&ta->dictionary)) != NULL) {
TAILQ_REMOVE(&ta->dictionary, entry, next);
free(entry->key);
free(entry->value);
free(entry);
}
bufferevent_free(ta->bev);
close(ta->fd);
free(ta);
}
void
smtp_greeting(struct smtp_ta *ta)
{
char *greeting;
greeting = smtp_response(ta, welcome);
if (greeting != NULL)
bufferevent_write(ta->bev, greeting, strlen(greeting));
}
/* Create a new SMTP transaction */
struct smtp_ta *
smtp_ta_new(int fd, struct sockaddr *sa, socklen_t salen,
struct sockaddr *lsa, socklen_t lsalen, int greeting)
{
struct smtp_ta *ta = calloc(1, sizeof(struct smtp_ta));
char line[1024];
char *ipname, *portname, *p;
struct tm tm;
time_t seconds;
int i, seed;
uint64_t hash;
if (ta == NULL)
goto error;
TAILQ_INIT(&ta->dictionary);
ta->state = EXPECT_HELO;
ta->fd = fd;
ta->bev = bufferevent_new(fd,
smtp_readcb, smtp_writecb, smtp_errorcb, ta);
if (ta->bev == NULL)
goto error;
/* Create our tiny dictionary */
name_from_addr(sa, salen, &ipname, &portname);
kv_add(&ta->dictionary, "$srcipaddress", ipname);
memcpy(&ta->sa, sa, salen);
ta->salen = salen;
/* Silly seed for domain name */
hash = 0xdeadbeefL;
p = (char *)&hash;
for (i = 0; i < strlen(ipname); i++) {
p[i % sizeof(hash)] ^= ipname[i];
}
hash = longhash1(hash);
/* See if we know who we are, too */
if (lsa != NULL) {
memcpy(&ta->lsa, lsa, lsalen);
ta->lsalen = lsalen;
name_from_addr(lsa, lsalen, &ipname, &portname);
kv_add(&ta->dictionary, "$dstipaddress", ipname);
for (i = 0; i < strlen(ipname); i++) {
p[i % sizeof(hash)] ^= ipname[i];
}
}
seed = longhash1(hash);
fprintf(stderr, "Seed: %s -> %x\n",
kv_find(&ta->dictionary, "$srcipaddress"), seed);
srand(seed);
seconds = time(NULL);
localtime_r(&seconds, &tm);
strftime(line, sizeof(line),
"%a, %e %b %Y %H:%M:%S %z (%Z)", &tm);
kv_add(&ta->dictionary, "$datum", line);
kv_add(&ta->dictionary, "$hostname", random_hostname());
/* Choose a mailer identity */
if (rand() % 2 == 0) {
ta->mailer_id = "sendmail";
} else {
ta->mailer_id = "postfix";
}
if (greeting)
smtp_greeting(ta);
bufferevent_enable(ta->bev, EV_READ);
fprintf(stderr, "%s: new SMTP instance to %s complete.\n",
__func__, kv_find(&ta->dictionary, "$srcipaddress"));
srand(time(NULL) ^ seed);
return (ta);
error:
if (ta != NULL)
free(ta);
fprintf(stderr, "%s: out of memory\n", __func__);
close(fd);
return (NULL);
}
void
accept_socket(int fd, short what, void *arg)
{
struct sockaddr_storage ss, lss;
socklen_t addrlen = sizeof(ss), laddrlen = sizeof(lss);
int nfd, res;
if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) {
fprintf(stderr, "%s: bad accept\n", __func__);
return;
}
/* Test our special subsystem magic */
res = fcntl(fd, F_XXX_GETSOCK, &lss, &laddrlen);
if (res != -1) {
/*
* We are running under honeyd and could figure out
* who we are. That's great.
*/
smtp_ta_new(nfd, (struct sockaddr *)&ss, addrlen,
(struct sockaddr *)&lss, laddrlen, 1);
} else {
smtp_ta_new(nfd, (struct sockaddr *)&ss, addrlen,
NULL, 0, 1);
}
}
void
smtp_bind_socket(struct event *ev, u_short port)
{
int fd;
if ((fd = make_socket(bind, SOCK_STREAM, "0.0.0.0", port)) == -1)
err(1, "%s: cannot bind socket: %d", __func__, port);
if (listen(fd, 10) == -1)
err(1, "%s: listen failed: %d", __func__, port);
/* Schedule the socket for accepting */
event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, NULL);
event_add(ev, NULL);
fprintf(stderr,
"Bound to port %d\n"
"Awaiting connections ... \n",
port);
}
syntax highlighted by Code2HTML, v. 0.9.1