33#include "timeoutconn.h"
43#define WHO "qmail-remote"
45#define QMTP_MAX 200000000
46#define HUGESMTPTEXT 1000
50#define PORT_SUBMISSION 587
51#define PORT_QMTPS 6209
54#define SMTP_TIMEOUT 1200
75static stralloc sauninit = {0};
115 if (buffer_puts(&
bs,s) == -1)
120 if (buffer_put(&
bs,
"\0",1) == -1)
135 for (i = 0; i <
sa->len; ++i) {
137 if (ch == 0)
continue;
138 if (ch < 33) ch =
'?';
139 if (ch > 126) ch =
'?';
140 if (buffer_put(&
bs,&ch,1) == -1)
_exit(0);
146 out(
"ZInvalid ipaddr in control/domainips (#4.3.0)\n");
151 out(
"ZOut of memory. (#4.3.0)\n");
156 out(
"ZSystem resources temporarily unavailable. (#4.3.0)\n");
161 out(
"ZCan't bind to local ip address: ");
168 out(
"ZSorry, I wasn't able to establish a SMTP connection: ");
175 out(
"ZSorry, I wasn't able to establish a QMTP connection: ");
182 out(
"ZUnable to read message. (#4.3.0)\n");
187 out(
"ZCNAME lookup failed temporarily for: ");
194 out(
"ZSorry, I couldn't find any host named: ");
201 out(
"ZSorry, I couldn't find a mail exchanger or IP address for: ");
203 out(
". Will try again. (#4.1.2)\n");
208 out(
"ZUnable to switch to home directory. (#4.3.0)\n");
213 out(
"ZUnable to read control files. (#4.3.0)\n");
218 out(
"DSMTP cannot transfer messages with partial final line. (#5.6.2)\n");
223 out(
"ZRecipient did not talk proper QMTP. (#4.3.0)\n");
228 out(
"Dqmail-remote was invoked improperly. (#5.3.5)\n");
233 out(
"DSorry, I couldn't find any host named: ");
240 out(
"DSorry, I couldn't find a mail exchanger or IP address for: ");
247 out(
"DSorry, I could no deliver mail to MX: ");
249 out(
" ; domain does not accept mails. (#5.1.10)\n");
254 out(
"DSorry. Although I'm listed as a best-preference MX or A for that host,\n\
255it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
260 out(
"ZSorry, no supported AUTH method found, trying later again. (#4.7.1)\n");
282 out(
"ZConnected to ");
284 out(
" but connection died. ");
327static stralloc smtptext = {0};
328static stralloc header = {0};
332 buffer_get(&
bf,ch,1);
335 if (!stralloc_append(&smtptext,ch))
temp_nomem();
350 if (ch !=
'-')
break;
351 while (ch !=
'\n')
get(&ch);
356 while (ch !=
'\n')
get(&ch);
364 if (smtptext.s)
if (smtptext.len) {
365 out(
"Remote host said: ");
366 for (i = 0; i < smtptext.len; ++i)
367 if (!smtptext.s[i]) smtptext.s[i] =
'?';
368 if (buffer_put(&
bs,smtptext.s,smtptext.len) == -1)
_exit(0);
373void quit(
char *prepend,
char *append)
375 buffer_putsflush(&
bo,
"QUIT\r\n");
405 if (
inbuf[
in] ==
'\r') {
in++;
continue; }
421 buffer_put(&
bo,
".\r\n",3);
488 if (case_startb(smtptext.s + i + 4,8,
"STARTTLS"))
return 1;
489 if (*(smtptext.s + i + 3) ==
' ')
return 0;
490 }
while ((i += str_chr(smtptext.s + i,
'\n') + 1) &&
491 (i < smtptext.len - 12));
499 STACK_OF(X509) *certs;
502 cert = SSL_get_peer_certificate(
ssl);
503 if (!cert) {
flagtls = 100;
return; }
505 if ((certs = SSL_get_peer_cert_chain(
ssl)) == NULL) {
506 certs = sk_X509_new_null();
508 sk_X509_push(certs,cert);
548 if (ncerts) sk_X509_free(certs);
560 if ((
unsigned char) ch[i++] > 127)
return 1;
570 stralloc receivedline = {0};
575 r = buffer_get(&
bi,&ch,1);
578 if (ch ==
'\r')
continue;
581 if (!stralloc_append(&header,
"\r"))
temp_nomem();
582 if (!stralloc_append(&header,
"\n"))
temp_nomem();
583 if (case_starts(receivedline.s,
"Date:"))
return 0;
584 if (case_starts(receivedline.s,
"Received: from"))
received++;
586 if (case_starts(receivedline.s,
" by ")) {
587 for (i = 6; i < receivedline.len - 6; ++i)
588 if (*(receivedline.s + i) ==
' ')
589 if (case_starts(receivedline.s + i + 1,
"with UTF8"))
return 1;
595 if (!stralloc_append(&header,&ch))
temp_nomem();
596 if (!stralloc_catb(&receivedline,&ch,1))
temp_nomem();
612 if (case_startb(smtptext.s + i + 4,4,
"SIZE"))
return 1;
613 if (*(smtptext.s + i + 3) ==
' ')
return 0;
614 }
while ((i += str_chr(smtptext.s + i,
'\n') + 1) &&
615 (i < smtptext.len - 8));
622 buffer_puts(&
bo,
"EHLO ");
624 buffer_puts(&
bo,
"\r\n");
628 buffer_puts(&
bo,
"HELO ");
630 buffer_puts(&
bo,
"\r\n");
635 if (
code >= 500)
quit(
"DConnected to ",
" but my name was rejected");
636 if (
code != 250)
quit(
"ZConnected to ",
" but my name was rejected");
643 buffer_puts(&
bo,
"STARTTLS\r\n");
653 quit(
"ZConnected to ",
" but STARTTLS was rejected");
659 buffer_puts(&
bo,
"MAIL FROM:<");
661 buffer_puts(&
bo,
">");
663 buffer_puts(&
bo,
" SMTPUTF8");
665 buffer_puts(&
bo,
" SIZE=");
668 buffer_puts(&
bo,
"\r\n");
685static const char hextab[] =
"0123456789abcdef";
695 for (i = 0; i < len; i++) {
697 if (c < 33 || c > 126 ||
c ==
'=' ||
c ==
'+') {
698 xch[0] = hextab[(
c >> 4) & 0x0f];
699 xch[1] = hextab[
c & 0x0f];
711 buffer_puts(&
bo,
"MAIL FROM:<");
713 buffer_puts(&
bo,
"> AUTH=");
716 buffer_puts(&
bo,
" SMTPUTF8");
718 buffer_puts(&
bo,
" SIZE=");
721 buffer_puts(&
bo,
"\r\n");
727 buffer_puts(&
bo,
"AUTH PLAIN\r\n");
730 if (
smtpcode() != 334)
quit(
"ZConnected to ",
" but authentication was rejected (AUTH PLAIN)");
739 buffer_puts(&
bo,
"\r\n");
744 case 432:
quit(
"DConnected to ",
" but password expired");
745 case 534:
quit(
"ZConnected to ",
" but authentication mechamism too weak (plain)");
746 default:
quit(
"ZConnected to ",
" but authentication was rejected (plain)");
753 buffer_puts(&
bo,
"AUTH LOGIN\r\n");
756 if (
smtpcode() != 334)
quit(
"ZConnected to ",
" but authentication was rejected (AUTH LOGIN)");
761 buffer_puts(&
bo,
"\r\n");
764 if (
smtpcode() != 334)
quit(
"ZConnected to ",
" but authentication was rejected (username)");
769 buffer_puts(&
bo,
"\r\n");
774 case 432:
quit(
"DConnected to ",
" but password expired");
775 case 534:
quit(
"ZConnected to ",
" but authentication mechanism is too weak (login)");
776 default:
quit(
"ZConnected to ",
" but authentication was rejected (login)");
787 buffer_puts(&
bo,
"AUTH CRAM-MD5\r\n");
790 if (
smtpcode() != 334)
quit(
"ZConnected to ",
" but authentication was rejected (AUTH CRAM-MD5)");
791 if (str_chr(smtptext.s + 4,
' ')) {
793 if (!stralloc_copyb(&
slop,smtptext.s + 4,smtptext.len - 5))
temp_nomem();
795 quit(
"ZConnected to ",
" but unable to base64decode challenge");
800 for (
j = 0;
j < 16;
j++) {
801 digascii[2 *
j] = hextab[(
unsigned char) digest[
j] >> 4];
802 digascii[2 *
j + 1] = hextab[(
unsigned char) digest[
j] & 0x0f];
815 buffer_puts(&
bo,
"\r\n");
820 case 432:
quit(
"DConnected to ",
" but password expired");
821 case 534:
quit(
"ZConnected to ",
" but authentication mechamism too weak (cram)");
822 default:
quit(
"ZConnected to ",
" but authentication was rejected (cram)");
832 if (case_startb(smtptext.s + i + 4,4,
"AUTH"))
833 for (i = 4; i < smtptext.len - 5; ++i) {
834 if (case_startb(smtptext.s + i,4,
"CRAM"))
836 if (case_startb(smtptext.s + i,5,
"LOGIN"))
838 if (case_startb(smtptext.s + i,5,
"PLAIN"))
841 }
while ((i += str_chr(smtptext.s + i,
'\n') + 1) &&
842 (i < smtptext.len - 12));
861 if (
code >= 500)
quit(
"DConnected to ",
" but sender was rejected");
862 if (
code == 421 ||
code == 450)
quit(
"ZConnected to ",
" but probably greylisted");
863 if (
code >= 400)
quit(
"ZConnected to ",
" but sender was rejected");
864 if (
code != 220)
quit(
"ZConnected to ",
" but greeting failed");
882 if (
code >= 500)
quit(
"DConnected to ",
" but sender was rejected");
883 if (
code >= 400)
quit(
"ZConnected to ",
" but sender was probably greylisted");
887 buffer_puts(&
bo,
"RCPT TO:<");
889 buffer_puts(&
bo,
">\r\n");
894 if (
code == 552)
quit(
"DConnected to ",
" but message size is too large");
895 if (
code == 452)
quit(
"ZConnected to ",
" however insufficient storage space available");
900 }
else if (
code >= 500) {
903 }
else if (
code >= 400) {
911 if (!flagbother)
quit(
"DGiving up on ",
"");
913 buffer_putsflush(&
bo,
"DATA\r\n");
916 if (
code >= 500)
quit(
"D",
" failed on DATA command");
917 if (
code >= 400)
quit(
"Z",
" failed on DATA command");
919 buffer_putflush(&
bo,header.s,header.len);
924 if (
code >= 500)
quit(
"D",
" failed after I sent the message");
925 if (
code >= 400)
quit(
"Z",
" failed after I sent the message");
927 case 100:
case 110:
quit(
"K",
" TLS transmitted message accepted");
break;
928 case 101:
case 111:
quit(
"K",
" TLS (verified CA) transmitted message accepted");
break;
929 case 102:
case 112:
quit(
"K",
" TLS (validated CA+DN*) transmitted message accepted");
break;
930 case 103:
case 113:
quit(
"K",
" TLS (validated CA+DN) transmitted message accepted");
break;
931 case 104:
case 114:
quit(
"K",
" TLS (CERT pinning) transmitted message accepted");
break;
932 case 105:
case 115:
quit(
"K",
" TLS (TLSA EE validated) transmitted message accepted");
break;
933 case 106:
case 116:
quit(
"K",
" TLS (TLSA TA validated) transmitted message accepted");
break;
934 case 107:
case 117:
quit(
"K",
" TLS (TLSA PKIX verified) transmitted message accepted");
break;
935 default:
quit(
"K",
" accepted message");
break;
945 unsigned long len = 0;
963 out(
"DMessage for: ");
outhost();
out(
" has zero bytes. Giving up.\n");
966 buffer_put(&
bo,
num,fmt_ulong(
num,len + 1));
967 buffer_put(&
bo,
":\n",2);
969 n = buffer_feed(&
bi);
970 if (n <= 0)
_exit(1);
971 x = buffer_PEEK(&
bi);
976 buffer_put(&
bo,
",",1);
979 buffer_put(&
bo,
num,fmt_ulong(
num,len));
980 buffer_put(&
bo,
":",1);
982 buffer_put(&
bo,
",",1);
987 buffer_put(&
bo,
num,fmt_ulong(
num,len));
988 buffer_put(&
bo,
":",1);
991 buffer_put(&
bo,
":",1);
993 buffer_put(&
bo,
",",1);
995 buffer_put(&
bo,
",",1);
1004 if (ch ==
':')
break;
1007 len = 10 * len + (ch -
'0');
1011 if ((ch !=
'Z') && (ch !=
'D') && (ch !=
'K'))
temp_proto();
1013 if (!stralloc_copyb(&smtptext,&ch,1))
temp_nomem();
1015 if (!stralloc_cats(&smtptext,
"qmtps:"))
temp_nomem();
1017 if (!stralloc_cats(&smtptext,
"qmtp:"))
temp_nomem();
1025 for (len = 0; len < smtptext.len; ++len) {
1026 ch = smtptext.s[len];
1027 if ((ch < 32) || (ch > 126)) smtptext.s[len] =
'?';
1031 smtptext.s[smtptext.len - 1] =
'\n';
1033 if (smtptext.s[0] ==
'K')
out(
"r");
1034 else if (smtptext.s[0] ==
'D') {
1042 if (buffer_put(&
bs,smtptext.s + 1,smtptext.len - 1) == -1)
temp_qmtpnoc();
1082 switch ((r = dns_cname(&cn,&
canonhost))) {
1132int main(
int argc,
char *
const argv[])
1134 static ipalloc ip = {0};
1135 stralloc netif = {0};
1140 unsigned long random;
1142 unsigned long prefme;
1162 i = str_chr(
sender.s,
'@');
1180 j = str_chr(localip,
'%');
1181 if (localip[
j] !=
'%')
j = 0;
1182 k = str_chr(localip,
'|');
1183 if (localip[
k] !=
'|')
k = 0;
1197 for (i = 0; i <=
sender.len; ++i)
1237 if (relayhost && !*relayhost) relayhost = 0;
1240 for (i = 0; i <=
host.len; ++i) {
1241 if ((i == 0) || (i ==
host.len) || (
host.s[i] ==
'.'))
1250 if (relayhost && !*relayhost) relayhost = 0;
1253 i = str_chr(relayhost,
'|');
1254 if (relayhost[i] ==
'|') {
1255 j = str_chr(relayhost + i + 1,
'|');
1256 if (relayhost[i +
j + 1] ==
'|') {
1258 relayhost[i +
j + 1] = 0;
1262 k = str_chr(relayhost + i +
j + 2,
'|');
1263 if (relayhost[i +
j +
k + 2] ==
'|') {
1264 relayhost[i +
j +
k + 2] = 0;
1265 localip = relayhost + i +
j +
k + 3;
1270 p = str_chr(relayhost,
';');
1271 if (relayhost[
p] ==
';') {
1272 if (relayhost[
p + 1] ==
's') {
flagsmtps = 1;
p++; }
1273 scan_ulong(relayhost +
p + 1,&
port);
1280 char *asciihost = 0;
1282 switch (idn2_lookup_u8(
host.s,(uint8_t**)&asciihost,IDN2_NFC_INPUT)) {
1283 case IDN2_OK:
break;
1395 if (fstat(0,&st) == -1)
quit(
"Z",
" unable to fstat stdin");
1408 random =
now() + (getpid() << 16);
1418#ifdef DEFERREDBOUNCES
1426 for (i = 0; i < ip.len; ++i)
1429 if (ip.ix[i].pref == 0 && ip.ix[i].mxh[0] ==
'.')
1432 if (ip.ix[i].pref < prefme)
1433 prefme = ip.ix[i].pref;
1436 if (relayhost) prefme = 300000;
1437 if (flagallaliases) prefme = 500000;
1440 i = str_chr(localip,
':');
1441 if (localip[i] ==
':') ip6flag = 1;
1445 for (i = 0; i < ip.len; ++i) {
1446 if (ip6flag == -1 && ip.ix[i].af == AF_INET6)
continue;
1447 if (ip6flag == 1 && ip.ix[i].af == AF_INET)
continue;
1448 if (ip.ix[i].pref < prefme)
break;
1456 for (i = 0; i < ip.len; ++i) {
1457 if (ip.ix[i].pref < prefme) {
1458 if (ip6flag == -1 && ip.ix[i].af == AF_INET6)
continue;
1459 if (ip6flag == 1 && ip.ix[i].af == AF_INET)
continue;
1460 if (
tcpto(&ip.ix[i]))
continue;
1462 smtpfd = socket(ip.ix[i].af,SOCK_STREAM,0);
1463 if (
smtpfd == -1)
continue;
1467 j = str_chr(localip,
':');
1468 if (localip[
j] ==
':') {
1470 if (byte_equal((
char *)ip.ix[i].addr.ip6.d,16,
ip6))
continue;
1471 ifidx = socket_getifidx(netif.s);
1475 if (byte_equal((
char *)ip.ix[i].addr.ip4.d,4,
ip4))
continue;
1482 if (ip.ix[i].af == AF_INET6)
1494 if (
flagtls == 9 && errno == EPROTO) {
1497 if (errno == ETIMEDOUT || errno == ECONNREFUSED || errno == EPROTO)
int b64encode(stralloc *in, stralloc *out)
int b64decode(const unsigned char *in, int len, stralloc *out)
int constmap_init(struct constmap *cm, char *s, int len, int flagcolon)
int control_readint(unsigned long *i, char *fn)
int control_rldef(stralloc *sa, char *fn, int flagme, char *def)
int control_readfile(stralloc *sa, char *fn, int flagme)
int stralloc_copys(stralloc *, char const *)
int dns_mxip(ipalloc *ia, stralloc *sa, unsigned long random)
int dns_ip(ipalloc *ia, stralloc *sa)
void c(char *, char *, char *, int, int, int)
void p(char *, char *, int, int, int)
void hmac_md5(char *text, int text_len, char *key, int key_len, char *digest)
int ipme_is(struct ip_mx *)
char tmpbuf[BUFSIZE_LINE]
GEN_ALLOC_readyplus(prioq, struct prioq_elt, p, len, a, i, n, x, 100, prioq_readyplus)
int utf8string(char *ch, int len)
ssize_t safewrite(int fd, char *buf, size_t len)
char inbuf[BUFFER_MTUSIZE]
struct constmap mapauthsenders
struct constmap mapqmtproutes
int xtext(stralloc *sa, char *s, int len)
char frombuf[BUFFER_SMALL]
void addrmangle(stralloc *saout, char *address, int *flagalias, int flagcname)
struct constmap mapdomainips
GEN_ALLOC_typedef(GEN_ALLOC_readyplus(saa, GEN_ALLOC_readyplus(stralloc, GEN_ALLOC_readyplus(sa, GEN_ALLOC_readyplus(len, GEN_ALLOC_readyplus(a)
struct constmap maptlsdestinations
void quit(char *prepend, char *append)
unsigned long verifydepth
void outsafe(stralloc *sa)
struct constmap mapdomaincerts
char bufsmall[BUFFER_SMALL]
unsigned long timeoutconnect
struct constmap mapsmtproutes
ssize_t saferead(int fd, char *buf, size_t len)
char outbuf[BUFFER_MTUSIZE]
int quote(stralloc *, stralloc *)
void tcpto_err(struct ip_mx *, int)
int tcpto(struct ip_mx *)
void temp_tlscipher(void)
void temp_tlsdigest(void)
void temp_tlspeerverify()
void temp_tlsamissing(void)
void temp_tlsainvalid(void)
void temp_tlscertfp(void)
void temp_invaliddigest(void)
int tlsa_check(const STACK_OF(X509) *, const stralloc, const unsigned long)
int tls_destination(stralloc)
tls_destination
int tls_domaincerts(stralloc)
int tls_fingerprint(X509 *, const char *, const int)
int tls_certkey(SSL_CTX *, const char *, const char *, char *)
int tls_checkpeer(SSL *, X509 *, const stralloc, const int, const int)
int tls_timeoutconn(int, int, int, SSL *)
int tls_timeoutwrite(int, int, int, SSL *, char *, int)
int tls_timeoutread(int, int, int, SSL *, char *, int)
int ssl_ciphers(SSL_CTX *, const char *)
SSL * ssl_new(SSL_CTX *, int)
int ssl_ca(SSL_CTX *, const char *, const char *, int)