Compare commits

...

5 Commits

Author SHA1 Message Date
c169a03ba1 stop xread/xwrite when no ret is zero - conn closed
Signed-off-by: AGentooCat <agentoocat@mail.i2p>
2025-06-12 07:57:26 +00:00
50f54a0a62 fix is_user_allowed whitelist check
Signed-off-by: AGentooCat <agentoocat@mail.i2p>
2025-06-12 07:56:53 +00:00
62843313fc fix crash when b32addr is not provided during bootup
Signed-off-by: AGentooCat <agentoocat@mail.i2p>
2025-06-12 07:56:38 +00:00
85d230aa8c fix crash when refreshing hosts
Signed-off-by: AGentooCat <agentoocat@mail.i2p>
2025-06-12 07:56:08 +00:00
1eac4594d9 implement mail dest cache usage
Signed-off-by: AGentooCat <agentoocat@mail.i2p>
2025-06-11 19:55:16 +00:00
6 changed files with 119 additions and 21 deletions

View File

@@ -109,6 +109,7 @@ sqlite3_stmt *cached_queue = NULL;
sqlite3_stmt *cached_pick_1 = NULL;
sqlite3_stmt *cached_pick_2 = NULL;
sqlite3_stmt *cached_pick_3 = NULL;
sqlite3_stmt *cached_gethost = NULL;
sqlite3_stmt *cached_hostaddr = NULL;
sqlite3_stmt *cached_failsend = NULL;
sqlite3_stmt *cached_notefail = NULL;
@@ -173,6 +174,12 @@ void opendb(const char *filename) {
ret = sqlite3_prepare_v3(conn, "INSERT INTO mail_lines (msgid,line) VALUES (:msgid,:line)", -1, SQLITE_PREPARE_PERSISTENT, &cached_line, NULL);
if (ret == SQLITE_OK)
ret = sqlite3_prepare_v3(conn, "INSERT INTO send_queue (msgid,addr) VALUES (:msgid,:addr)", -1, SQLITE_PREPARE_PERSISTENT, &cached_queue, NULL);
if (ret == SQLITE_OK)
ret = sqlite3_prepare_v3(conn,
"SELECT addr,ttl,priority,weight,is_i2p,last_used FROM mail_dests "
"WHERE host = ? "
"AND (last_used == -1 OR ttl == -1 OR last_used + ttl > unixepoch()) "
"ORDER BY priority ASC, weight DESC", -1, SQLITE_PREPARE_PERSISTENT, &cached_gethost, NULL);
if (ret == SQLITE_OK)
ret = sqlite3_prepare_v3(conn,
"SELECT msgid FROM send_queue "
@@ -270,6 +277,7 @@ void closedb() {
sqlite3_finalize(cached_failsend);
sqlite3_finalize(cached_notefail);
sqlite3_finalize(cached_hostaddr);
sqlite3_finalize(cached_gethost);
sqlite3_finalize(cached_pick_3);
sqlite3_finalize(cached_pick_2);
sqlite3_finalize(cached_pick_1);
@@ -281,6 +289,61 @@ void closedb() {
conn = NULL;
}
#define CLEARSQL(x) { \
sqlite3_reset(x); \
sqlite3_clear_bindings(x); \
}
#define CHKSQL(x) while ((ret = x) != SQLITE_OK) { if (ret == SQLITE_DONE) break; if (ret == SQLITE_BUSY) continue; err = (char*)sqlite3_errstr(ret); goto RETPATH; }
#define BINDSTR(x, y, z) CHKSQL(sqlite3_bind_text(x, y, z, -1, SQLITE_STATIC))
int get_cached_mail_hosts(char *host, struct I2Host *addrs) {
int ret;
char *err, *mail_addr;
#define RETPATH cachedhostfail
BINDSTR(cached_gethost, 1, host);
addrs->mailsrvl = 0;
while ((ret = sqlite3_step(cached_gethost)) == SQLITE_ROW)
addrs->mailsrvl++;
if (ret != SQLITE_DONE)
goto cachedhostfail;
if (addrs->mailsrvl == 0) {
CLEARSQL(cached_gethost);
return 0;
}
sqlite3_reset(cached_gethost);
if (!(addrs->mailsrv = malloc(addrs->mailsrvl * sizeof(struct SMTPAddr)))) {
ret = SQLITE_NOMEM;
goto cachedhostfail;
}
memset(addrs->mailsrv, 0, addrs->mailsrvl * sizeof(struct SMTPAddr));
addrs->mailsrvl = 0;
while ((ret = sqlite3_step(cached_gethost)) == SQLITE_ROW) {
if (!(mail_addr = strdup(sqlite3_column_text(cached_gethost, 0)))) {
ret = SQLITE_NOMEM;
goto cachedhostfail;
}
addrs->mailsrv[addrs->mailsrvl].addr = mail_addr;
addrs->mailsrv[addrs->mailsrvl].ttl = sqlite3_column_int(cached_gethost, 1);
addrs->mailsrv[addrs->mailsrvl].priority = sqlite3_column_int(cached_gethost, 2);
addrs->mailsrv[addrs->mailsrvl].weight = sqlite3_column_int(cached_gethost, 3);
addrs->mailsrv[addrs->mailsrvl].is_i2p = sqlite3_column_int(cached_gethost, 4);
addrs->mailsrvl++;
}
CLEARSQL(cached_gethost);
return 1;
cachedhostfail:
CLEARSQL(cached_gethost);
if (addrs->mailsrv) {
for (long i = 0; i < addrs->mailsrvl; i++)
xfree(addrs->mailsrv[i].addr);
free(addrs->mailsrv);
}
addrs->mailsrvl = 0;
return -ret;
#undef RETPATH
}
int commit_mail(int is_incoming, struct Mail *mail, char *buf, char **tos, long tlen) {
// the mail must have "From", "To" and "Subject" fields
// if "Message-Id" is not given then we generate one
@@ -319,10 +382,6 @@ int commit_mail(int is_incoming, struct Mail *mail, char *buf, char **tos, long
// it's commiting time
char *err = NULL;
int ret = sqlite3_exec(conn, "BEGIN TRANSACTION", NULL, NULL, &err);
#define CLEARSQL(x) { \
sqlite3_reset(x); \
sqlite3_clear_bindings(x); \
}
if (ret != SQLITE_OK) {
incommitfail:
if (mid_alloc)
@@ -337,9 +396,6 @@ incommitfail:
}
#define RETPATH incommitfail
#define CHKSQL(x) while ((ret = x) != SQLITE_OK) { if (ret == SQLITE_DONE) break; if (ret == SQLITE_BUSY) continue; err = (char*)sqlite3_errstr(ret); goto RETPATH; }
#define BINDSTR(x, y, z) CHKSQL(sqlite3_bind_text(x, y, z, -1, SQLITE_STATIC))
int idx[] = {
sqlite3_bind_parameter_index(cached_mail, ":from"),
sqlite3_bind_parameter_index(cached_mail, ":to"),
@@ -605,6 +661,7 @@ int sendmsgid_tohost(struct I2Host *host, struct Mail *thand, char **err) {
long llen = 0;
char *serv32 = NULL;
restart_sendhost:
for (long i = 0; i < host->mailsrvl; i++) {
if ((cfd = conn2host(host->mailsrv[i].addr, &conerr)) > -1) {
serv32 = host->mailsrv[i].addr;
@@ -614,6 +671,10 @@ int sendmsgid_tohost(struct I2Host *host, struct Mail *thand, char **err) {
xfree(conerr);
}
if (cfd < 0) {
if (host->from_cache) {
refresh_hosts(host);
goto restart_sendhost;
}
asprintf(err, "SAM returned: %s", conerr);
xfree(conerr);
return -1;
@@ -976,12 +1037,12 @@ mailsendfail:
}
if (idx == -1) {
if (!(tos[tol] = lookup_host(dest, NULL))) {
if (!(tos[tol] = lookup_host(dest, NULL, 0))) {
for (long i = 0; i < tol; i++)
free_lookup(tos[i]);
free(tos);
ret = SQLITE_OK;
err = "failed to find a destination host online";
err = "failed to find a destination host";
goto mailsendfail;
}
*(dest - 1) = 0;

View File

@@ -2,6 +2,7 @@
#define MAIL_H
#include <sqlite3.h>
struct I2Host; // to prevent undeclared structs around this, smtp.h and sam.h
struct Header {
long alen;
@@ -32,6 +33,7 @@ struct SizedMsgid {
int deleted;
};
int get_cached_mail_hosts(char *host, struct I2Host *addrs);
int parse_save_header(struct Mail *mail, long len, char *buf);
void freemail(struct Mail *mail);

View File

@@ -624,7 +624,21 @@ skiphostnote:
commit_hostaddrs(host);
}
struct I2Host *lookup_host(char *b32addr, char *knownas) {
void refresh_hosts(struct I2Host *host) {
if (host->mailsrv) {
for (long i = 0; i < host->mailsrvl; i++)
xfree(host->mailsrv[i].addr);
free(host->mailsrv);
}
host->mailsrvl = host->from_cache = 0;
if (!host->_under) {
host->_under = samcomm(ctlsock, "NAMING", "LOOKUP", "NAME", host->b32addr, "OPTIONS", "true", NULL);
if (!host->_under || !host->_under->success) return;
}
lookup_mail(host);
}
struct I2Host *lookup_host(char *b32addr, char *knownas, int force_lookup) {
if ((b32addr && knownas) || !(b32addr || knownas))
log(CRIT, "[BUG] both b32addr+knownas provided or omitted");
while (ctlsock == -1)
@@ -633,7 +647,17 @@ struct I2Host *lookup_host(char *b32addr, char *knownas) {
struct I2Host *host = malloc(sizeof(struct I2Host));
if (!host) goto samlookfail;
memset(host, 0, sizeof(*host));
if (b32addr && !(host->b32addr = strdup(b32addr))) goto samlookfail;
if (!force_lookup && b32addr) {
int cachret = get_cached_mail_hosts(b32addr, host);
if (cachret > 0) {
host->from_cache = 1;
return host;
}
if (cachret < 0)
log(WARN, "failed to get cached mail dests: %d/%s", -cachret, sqlite3_errstr(-cachret));
}
if (!(host->_under = samcomm(ctlsock, "NAMING", "LOOKUP", "NAME", b32addr ? b32addr : knownas, "OPTIONS", "true", NULL)))
goto samlookfail;
@@ -641,7 +665,7 @@ struct I2Host *lookup_host(char *b32addr, char *knownas) {
if (!host->_under->success) goto samlookfail;
host->b64dest = get_samval(host->_under, "VALUE");
if (!(host->b32addr = b64to32(host->b64dest))) goto samlookfail;
if (!b32addr && !(host->b32addr = b64to32(host->b64dest))) goto samlookfail;
lookup_mail(host);
return host;
@@ -709,7 +733,7 @@ void opensam(port_t port, char *me, char *dest) {
log(CRIT, "SAM failed to create session tunnels: %s", get_samval(sesreply, "MESSAGE") ? get_samval(sesreply, "MESSAGE") : get_samval(sesreply, "RESULT"));
free_sammsg(sesreply);
mehost = lookup_host(NULL, me);
mehost = lookup_host(NULL, me, 1);
if (!mehost)
log(WARN, "configured hostname \"%s\" couldn't be looked up", me);
else if (!mehost->mailsrvl)

View File

@@ -12,7 +12,7 @@ struct I2Host {
char *knownas;
struct SMTPAddr *mailsrv;
long mailsrvl;
int is_self, is_failed;
int is_self, is_failed, from_cache;
struct SAMMessage *_under;
@@ -25,7 +25,8 @@ extern struct I2Host *mehost;
extern struct linepoll_t *sam_sockpoll;
char *b64to32(char *dest);
struct I2Host *lookup_host(char *b32addr, char *knownas);
void refresh_hosts(struct I2Host *host);
struct I2Host *lookup_host(char *b32addr, char *knownas, int force_lookup);
void free_lookup(struct I2Host *host);
int is_host_known(char *hostname);
int conn2host(char *b32host, char **err);

View File

@@ -203,7 +203,7 @@ int is_user_allowed(char *user) {
// conf.service.allowed_users is a whilelist if set
// .disallowed_users is a blacklist over the whitelist if set
int is_good = gloconf->allowed_users != NULL;
int is_good = gloconf->allowed_users == NULL;
if (!is_good) {
// whitelist active; check there
for (long i = 0; gloconf->allowed_users[i]; i++)
@@ -394,9 +394,15 @@ int smtp_line(struct lineconn_t *hand) {
goto smtplinefail;
char *b32peer = b64to32(hand->edata);
if (!b32peer) goto smtplinefail;
struct I2Host *host = lookup_host(NULL, hostname);
int host_good = 1;
int force_lookup = 0;
restart_lookup:
int host_good = 1, found = 0;
struct I2Host *host = lookup_host(NULL, hostname, force_lookup);
if (!host || host->mailsrvl == 0) {
if (host && host->mailsrvl == 0 && host->from_cache) {
force_lookup = 1;
goto restart_lookup;
}
host_good = 0;
// run
if (!gloconf->allow_unverified_hosts) {
@@ -405,13 +411,16 @@ int smtp_line(struct lineconn_t *hand) {
goto smtplinefail;
}
}
int found = 0;
for (long i = 0; i < host->mailsrvl; i++) {
if (!strcmp(b32peer, host->mailsrv[i].addr)) {
found = 1;
break;
}
}
if (host->from_cache && !found) {
force_lookup = 1;
goto restart_lookup;
}
*(hostname - 1) = 0;
if (!strcmp(com->argv[0], "itoomail-system")) {

View File

@@ -38,13 +38,14 @@ void strip_crlf(const char *text) {
#define XIO(name, func, ...) ssize_t name(__VA_ARGS__) { \
ssize_t ret = 0; \
size_t rem = len; \
size_t rem = len, done = 0; \
while (rem > 0) { \
ret = func(fd, &line[len - rem], rem); \
if (ret == 0) break; \
if (ret < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) return ret; \
if (ret > 0) rem -= ret; \
if (ret > 0) { rem -= ret; done += ret; } \
} \
return len; \
return done; \
}
XIO(xread, read, int fd, void *line, size_t len);
XIO(xwrite, write, int fd, const char *line, size_t len);