implement mail dest cache usage

Signed-off-by: AGentooCat <agentoocat@mail.i2p>
This commit is contained in:
2025-06-11 19:55:16 +00:00
parent 5d1768b8b6
commit 1eac4594d9
5 changed files with 109 additions and 17 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,17 @@ 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;
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 +643,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 +661,6 @@ 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;
lookup_mail(host);
return host;
@ -709,7 +728,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

@ -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")) {