summaryrefslogtreecommitdiff
path: root/sqmail-4.3.07/src/qmail-remote.c
diff options
context:
space:
mode:
Diffstat (limited to 'sqmail-4.3.07/src/qmail-remote.c')
-rw-r--r--sqmail-4.3.07/src/qmail-remote.c1476
1 files changed, 0 insertions, 1476 deletions
diff --git a/sqmail-4.3.07/src/qmail-remote.c b/sqmail-4.3.07/src/qmail-remote.c
deleted file mode 100644
index d2f08dd..0000000
--- a/sqmail-4.3.07/src/qmail-remote.c
+++ /dev/null
@@ -1,1476 +0,0 @@
-#ifdef IDN2
-#include <idn2.h>
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include "sig.h"
-#include "stralloc.h"
-#include "buffer.h"
-#include "scan.h"
-#include "case.h"
-#include "byte.h"
-#include "logmsg.h"
-#include "qmail.h"
-#include "auto_qmail.h"
-#include "control.h"
-#include "dns.h"
-#include "alloc.h"
-#include "genalloc.h"
-#include "quote.h"
-#include "fmt.h"
-#include "ip.h"
-#include "ipalloc.h"
-#include "ipme.h"
-#include "str.h"
-#include "now.h"
-#include "exit.h"
-#include "constmap.h"
-#include "tcpto.h"
-#include "timeout.h"
-#include "timeoutconn.h"
-#include "base64.h"
-#include "socket_if.h"
-#include "ucspissl.h"
-#include "hmac_md5.h"
-#include "tls_remote.h"
-#include "tls_errors.h"
-#include "tls_timeoutio.h"
-#include "uint_t.h"
-
-#define WHO "qmail-remote"
-
-#define QMTP_MAX 200000000 /* 190 MB for QMTP */
-#define HUGESMTPTEXT 1000 /* RFC 5322; was 5000 chars/line */
-#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
-#define PORT_QMTP 209
-#define PORT_SMTPS 465
-#define PORT_SUBMISSION 587
-#define PORT_QMTPS 6209
-#define VERIFYDEPTH 1
-#define TCP_TIMEOUT 60
-#define SMTP_TIMEOUT 1200
-
-unsigned long port = PORT_SMTP;
-
-/** @file qmail-remote.c -- versatile SMTP(S)/QMTP(S) client */
-
-int flagauth = 0; /* 1 = login; 2 = plain; 3 = crammd5 */
-int flagsmtps = 0; /* RFC 8314 - 'implicit TLS' */
-int flagtlsdomain = 0; /* 0 = no; 1 = yes; 2 = cert */
-int flagtls = 0; /* flagtls: XYZ
- (mode) Z: -2 = rejected; -1 = not; 0 = no, default; Z > 0 see tls_remote.c
- (prot) Y: 0 = StartTLS; 1 = SMTPS; 2 = QMTPS
- (active) X: 1 = running TLS connection (after DNS lookup)
- (done) Z: 1: CA chain; 2: Cert wildname; 3: Cert exactname;
- 4: Cert fingerprint; 5: TLSA record */
-int flagverify = 0; /* 1 = verify Cert against CA; 2 = verify against Dir; 3 = triggerd by TLSA;
- -2 = Cert pinning; -1 = no TLSA validation */
-int flagutf8 = 0;
-
-GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
-GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
-static stralloc sauninit = {0};
-
-stralloc helohost = {0};
-stralloc eaihost = {0};
-stralloc host = {0};
-stralloc idnhost = {0};
-stralloc sender = {0};
-stralloc canonhost = {0};
-stralloc remotehost = {0};
-stralloc canonbox = {0};
-stralloc senddomain = {0};
-stralloc sendip = {0};
-
-stralloc domainips = {0};
-struct constmap mapdomainips;
-char ip4[4];
-char ip6[16];
-uint32 ifidx = 0;
-char *authsender = 0;
-
-stralloc smtproutes = {0};
-struct constmap mapsmtproutes;
-stralloc qmtproutes = {0};
-struct constmap mapqmtproutes;
-
-saa reciplist = {0};
-stralloc recip = {0};
-
-char msgsize[FMT_ULONG];
-unsigned long msize = 0;
-struct ip_mx partner;
-
-SSL *ssl;
-SSL_CTX *ctx;
-
-char bufsmall[BUFFER_SMALL];
-buffer bs = BUFFER_INIT(write,1,bufsmall,sizeof(bufsmall));
-
-void out(char *s)
-{
- if (buffer_puts(&bs,s) == -1)
- _exit(0);
- }
-void zero()
-{
- if (buffer_put(&bs,"\0",1) == -1)
- _exit(0);
-}
-void zerodie()
-{
- zero();
- buffer_flush(&bs);
- if (ssl) tls_exit(ssl);
- _exit(0);
-}
-
-void outsafe(stralloc *sa)
-{
- int i;
- char ch;
- for (i = 0; i < sa->len; ++i) {
- ch = sa->s[i];
- if (ch == 0) continue;
- if (ch < 33) ch = '?';
- if (ch > 126) ch = '?';
- if (buffer_put(&bs,&ch,1) == -1) _exit(0);
- }
-}
-
-void temp_noip()
-{
- out("ZInvalid ipaddr in control/domainips (#4.3.0)\n");
- zerodie();
-}
-void temp_nomem()
-{
- out("ZOut of memory. (#4.3.0)\n");
- zerodie();
-}
-void temp_oserr()
-{
- out("ZSystem resources temporarily unavailable. (#4.3.0)\n");
- zerodie();
-}
-void temp_osip()
-{
- out("ZCan't bind to local ip address: ");
- outsafe(&sendip);
- out(". (#4.3.0)\n");
- zerodie();
-}
-void temp_noconn()
-{
- out("ZSorry, I wasn't able to establish an SMTP connection: ");
- outsafe(&canonhost);
- out(". (#4.3.0)\n");
- zerodie();
-}
-void temp_qmtpnoc()
-{
- out("ZSorry, I wasn't able to establish an QMTP connection: ");
- outsafe(&canonhost);
- out(". (#4.3.1)\n");
- zerodie();
-}
-void temp_read()
-{
- out("ZUnable to read message. (#4.3.0)\n");
- zerodie();
-}
-void temp_dnscanon()
-{
- out("ZCNAME lookup failed temporarily for: ");
- outsafe(&canonhost);
- out(". (#4.4.3)\n");
- zerodie();
-}
-void temp_dns()
-{
- out("ZSorry, I couldn't find any host named: ");
- outsafe(&host);
- out(". (#4.1.2)\n");
- zerodie();
-}
-void temp_nomx()
-{
- out("ZSorry, I couldn't find a mail exchanger or IP address for: ");
- outsafe(&host);
- out(". Will try again. (#4.1.2)\n");
- zerodie();
-}
-void temp_chdir()
-{
- out("ZUnable to switch to home directory. (#4.3.0)\n");
- zerodie();
-}
-void temp_control()
-{
- out("ZUnable to read control files. (#4.3.0)\n");
- zerodie();
-}
-void perm_partialline()
-{
- out("DSMTP cannot transfer messages with partial final line. (#5.6.2)\n");
- zerodie();
-}
-void temp_proto()
-{
- out("ZRecipient did not talk proper QMTP. (#4.3.0)\n");
- zerodie();
-}
-void perm_usage()
-{
- out("Dqmail-remote was invoked improperly. (#5.3.5)\n");
- zerodie();
-}
-void perm_dns()
-{
- out("DSorry, I couldn't find any host named: ");
- outsafe(&host);
- out(". (#5.1.2)\n");
- zerodie();
-}
-void perm_nomx()
-{
- out("DSorry, I couldn't find a mail exchanger or IP address for: ");
- outsafe(&host);
- out(". (#5.4.4)\n");
- zerodie();
-}
-void perm_ambigmx()
-{
- out("DSorry. Although I'm listed as a best-preference MX or A for that host,\n\
-it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
- zerodie();
-}
-void err_authprot()
-{
- out("ZSorry, no supported AUTH method found, trying later again. (#4.7.1)\n");
- zerodie();
-}
-
-void outhost()
-{
- char ipaddr[IPFMT];
- int len;
-
- switch (partner.af) {
- case AF_INET:
- len = ip4_fmt(ipaddr,(char *)&partner.addr.ip4.d); break;
- case AF_INET6:
- len = ip6_fmt(ipaddr,(char *)&partner.addr.ip6.d); break;
- }
- if (buffer_put(&bs,ipaddr,len) == -1) _exit(0);
-}
-
-int flagcritical = 0;
-
-void dropped()
-{
- out("ZConnected to ");
- outhost();
- out(" but connection died. ");
- if (flagcritical) out("Possible duplicate! ");
- out("(#4.4.2)\n");
- zerodie();
-}
-
-int timeoutconnect = TCP_TIMEOUT;
-int smtpfd;
-int timeout = SMTP_TIMEOUT;
-
-ssize_t saferead(int fd,char *buf,int len)
-{
- int r;
- if (ssl) {
- r = tls_timeoutread(timeout,smtpfd,smtpfd,ssl,buf,len);
- if (r < 0) temp_tlserr();
- } else {
- r = timeoutread(timeout,smtpfd,buf,len);
- }
- if (r <= 0) dropped();
- return r;
-}
-
-ssize_t safewrite(int fd,char *buf,int len)
-{
- int r;
- if (ssl) {
- r = tls_timeoutwrite(timeout,smtpfd,smtpfd,ssl,buf,len);
- if (r < 0) temp_tlserr();
- } else {
- r = timeoutwrite(timeout,smtpfd,buf,len);
- }
- if (r <= 0) dropped();
- return r;
-}
-
-char inbuf[BUFSIZE_LINE];
-buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf));
-char outbuf[BUFSIZE_MESS];
-buffer bo = BUFFER_INIT(safewrite,-1,outbuf,sizeof(outbuf));
-char frombuf[BUFFER_SMALL];
-buffer bf = BUFFER_INIT(saferead,-1,frombuf,sizeof(frombuf));
-
-static stralloc smtptext = {0};
-static stralloc header = {0};
-
-void get(char *ch)
-{
- buffer_get(&bf,ch,1);
- if (*ch != '\r')
- if (smtptext.len < HUGESMTPTEXT)
- if (!stralloc_append(&smtptext,ch)) temp_nomem();
-}
-
-unsigned long smtpcode()
-{
- unsigned char ch;
- unsigned long code;
-
- if (!stralloc_copys(&smtptext,"")) temp_nomem();
-
- get(&ch); code = ch - '0';
- get(&ch); code = code * 10 + (ch - '0');
- get(&ch); code = code * 10 + (ch - '0');
- for (;;) {
- get(&ch);
- if (ch != '-') break;
- while (ch != '\n') get(&ch);
- get(&ch);
- get(&ch);
- get(&ch);
- }
- while (ch != '\n') get(&ch);
-
- return code;
-}
-
-void outsmtptext()
-{
- int i;
- if (smtptext.s) if (smtptext.len) {
- out("Remote host said: ");
- for (i = 0; i < smtptext.len; ++i)
- if (!smtptext.s[i]) smtptext.s[i] = '?';
- if (buffer_put(&bs,smtptext.s,smtptext.len) == -1) _exit(0);
- smtptext.len = 0;
- }
-}
-
-void quit(char *prepend,char *append)
-{
- buffer_putsflush(&bo,"QUIT\r\n");
- /* waiting for remote side is just too ridiculous */
- out(prepend);
- outhost();
- out(append);
- out(".\n");
- outsmtptext();
- zerodie();
-}
-
-void blast()
-{
- int r;
- int in;
- int out;
- int eom = 1; // end-of-message <CRLF>.<CRLF>
- char tmpbuf[BUFSIZE_MESS + 2]; // intermediate write buffer
-
-// New blast code; inspired by Bruce Guenter's 'fastremote patch' (2005)
-
- while ((r = buffer_get(&bi,inbuf,sizeof(inbuf)))) { // read into buffer
- if (r == -1) temp_read();
-
- for (in = out = 0; in < r;) {
- if (eom && inbuf[in] == '.') {
- tmpbuf[out++] = '.';
- tmpbuf[out++] = inbuf[in++];
- }
- eom = 0;
- while (in < r) {
- if (inbuf[in] == '\r') { in++; continue; } // CR is DKIM input
- if (inbuf[in] == '\n') {
- eom = 1;
- in++;
- tmpbuf[out++] = '\r';
- tmpbuf[out++] = '\n';
- break;
- }
- tmpbuf[out++] = inbuf[in++];
- }
- }
- if (out) buffer_put(&bo,tmpbuf,out);
- }
-
- if (!eom) perm_partialline();
- flagcritical = 1;
- buffer_put(&bo,".\r\n",3); // LF seen; finish with .<CRLF>
- buffer_flush(&bo);
-}
-
-/* this file is too long -------------------------------------- client TLS */
-
-stralloc cafile = {0};
-stralloc cadir = {0};
-stralloc certfile = {0};
-stralloc keyfile = {0};
-stralloc keypwd = {0};
-stralloc ciphers = {0};
-
-char *tlsdestinfo = 0;
-char *tlsdomaininfo = 0;
-
-stralloc domaincerts = {0};
-struct constmap mapdomaincerts;
-stralloc tlsdestinations = {0};
-struct constmap maptlsdestinations;
-unsigned long verifydepth = VERIFYDEPTH;
-
-void tls_init()
-{
- ctx = ssl_client();
- ssl_errstr();
- if (!ctx) temp_tlsctx();
-
-/* Fetch CA infos for dest */
-
- if (flagverify > 0)
- if (cafile.len || cadir.len)
- if (!ssl_ca(ctx,cafile.s,cadir.s,(int) verifydepth)) temp_tlsca();
-
- if (ciphers.len)
- if (!ssl_ciphers(ctx,ciphers.s)) temp_tlscipher();
-
-/* Prepare for Certificate Request */
-
- if (flagtlsdomain == 2)
- switch (tls_certkey(ctx,certfile.s,keyfile.s,keypwd.s)) {
- case 0: break;
- case -1: temp_tlscert();
- case -2: temp_tlskey();
- case -3: temp_tlschk();
- }
-
-/* Set SSL Context */
-
- ssl = ssl_new(ctx,smtpfd);
- if (!ssl) temp_tlsctx();
-
-/* Setup SSL FDs */
-
- if (!tls_conn(ssl,smtpfd)) temp_tlscon();
-
-/* Go on in none-blocking mode */
-
- if (tls_timeoutconn(timeout,smtpfd,smtpfd,ssl) <= 0)
- temp_tlserr();
-}
-
-int starttls_peer()
-{
- int i = 0;
-
- if (smtptext.len > 16)
- for (i = 0; i < smtptext.len - 8; ++i)
- if (case_starts(smtptext.s + i,"STARTTLS")) return 1;
-
- return 0;
-}
-
-void tls_peercheck()
-{
- X509 *cert;
- STACK_OF(X509) *certs;
-
- cert = SSL_get_peer_certificate(ssl);
- if (!cert) { flagtls = 100; return; }
-
- if ((certs = SSL_get_peer_cert_chain(ssl)) == NULL) {
- certs = sk_X509_new_null();
- sk_X509_push(certs, cert);
- }
-
- if (flagverify == -2) { // fingerprinting is silent
- if (cafile.len) case_lowerb(cafile.s,cafile.len);
- switch (tls_fingerprint(cert,cafile.s + 1,cafile.len - 2)) {
- case -1: temp_tlspeercert();
- case -2: temp_tlsdigest();
- case -3: temp_invaliddigest();
- case 0: temp_tlscertfp();
- case 1: flagtls = 104; break;
- }
- }
-
- if (flagverify >= 0) { // TLSA is default
- switch (tlsa_check(certs,remotehost,port)) {
- case -4: temp_tlsamissing(); break; /* FIXME */
- case -3: temp_tlsainvalid(); break;
- case -2: break; // unsupported type; may happen
- case -1: break; // weird TLSA record
- case 0: break; // no TLSA record given
- case 1: case 2: flagtls = 107; flagverify = 3; break; // full certchain available (-PKIX)
- case 3: flagtls = 106; flagverify = 0; break; // TA-CA; verify wont work
- case 4: flagtls = 105; flagverify = 0; break; // Endpoint only
- }
- }
-
- if (flagverify > 0) {
- switch (tls_checkpeer(ssl,cert,remotehost,flagtls,flagverify)) {
- case -1: temp_tlspeercert();
- case -2: temp_tlspeerverify();
- case -3: temp_tlspeervalid();
- case 1: flagtls = 101; break;
- case 2: flagtls = 102; break;
- case 3: flagtls = 103; break;
- }
- }
-
- if (flagtls < 100) flagtls = 100;
-
- X509_free(cert);
- X509_free(certs);
-
- return;
-}
-
-/* this file is too long --------------------------------------- smtp UTF8 */
-
-int utf8string(unsigned char *ch,int len)
-{
- int i = 0;
- while (i < len)
- if (ch[i++] > 127) return 1;
- return 0;
-}
-
-int utf8received()
-{
- int r;
- int i;
- int received = 0;
- char ch;
- stralloc receivedline = {0};
-
-/* we consider only our own last written header */
-
- for (;;) {
- r = buffer_get(&bi,&ch,1);
- if (r == 0) break;
- if (r == -1) temp_read();
- if (ch == '\r') continue; // DKIM
-
- if (ch == '\n') {
- if (!stralloc_append(&header,"\r")) temp_nomem(); /* received.c does not add '\r' */
- if (!stralloc_append(&header,"\n")) temp_nomem();
- if (case_starts(receivedline.s,"Date:")) return 0; /* header to quit asap */
- if (case_starts(receivedline.s,"Received: from")) received++; /* found Received header */
- if (received) {
- if (case_starts(receivedline.s," by ")) {
- for (i = 6; i < receivedline.len - 6; ++i)
- if (*(receivedline.s + i) == ' ')
- if (case_starts(receivedline.s + i + 1,"with UTF8")) return 1;
- return 0;
- }
- }
- if (!stralloc_copys(&receivedline,"")) temp_nomem();
- } else {
- if (!stralloc_append(&header,&ch)) temp_nomem();
- if (!stralloc_catb(&receivedline,&ch,1)) temp_nomem();
- }
- }
- return 0;
-}
-
-/* this file is too long -------------------------------------- smtp client */
-
-unsigned long code;
-int flagsize = 0;
-
-int smtp_size()
-{
- int i;
- if (smtptext.len > 10)
- for (i = 0; i < smtptext.len; ++i) {
- if (case_starts(smtptext.s + i,"SIZE ")) return 1;
- }
- return 0;;
-}
-
-void smtp_greeting()
-{
- buffer_puts(&bo,"EHLO ");
- buffer_put(&bo,helohost.s,helohost.len);
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-
- if (smtpcode() != 250) {
- buffer_puts(&bo,"HELO ");
- buffer_put(&bo,helohost.s,helohost.len);
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-
- code = smtpcode();
- authsender = 0;
- if (code >= 500) quit("DConnected to "," but my name was rejected");
- if (code != 250) quit("ZConnected to "," but my name was rejected");
- }
- flagsize = smtp_size();
-}
-
-void smtp_starttls()
-{
- buffer_puts(&bo,"STARTTLS\r\n");
- buffer_flush(&bo);
-
- if (smtpcode() == 220) {
- tls_init();
- tls_peercheck();
- smtp_greeting();
- }
- else {
- flagtls = -2;
- quit("ZConnected to "," but STARTTLS was rejected");
- }
-}
-
-void mailfrom()
-{
- buffer_puts(&bo,"MAIL FROM:<");
- buffer_put(&bo,sender.s,sender.len);
- buffer_puts(&bo,">");
- if (flagutf8 || utf8received())
- buffer_puts(&bo," SMTPUTF8");
- if (flagsize && msize) {
- buffer_puts(&bo," SIZE=");
- buffer_puts(&bo,msgsize);
- }
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-}
-
-/* this file is too long -------------------------------------- client auth */
-
-stralloc authsenders = {0};
-struct constmap mapauthsenders;
-
-stralloc user = {0};
-stralloc pass = {0};
-stralloc auth = {0};
-stralloc chal = {0};
-stralloc slop = {0};
-stralloc plain = {0};
-stralloc xuser = {0};
-
-static const char hextab[] = "0123456789abcdef";
-
-int xtext(stralloc *sa,char *s,int len)
-{
- int i;
- unsigned char c;
- char xch[2];
-
- if (!stralloc_copys(sa,"")) temp_nomem();
-
- for (i = 0; i < len; i++) {
- c = s[i];
- if (c < 33 || c > 126 || c == '=' || c == '+') {
- xch[0] = hextab[(c >> 4) & 0x0f];
- xch[1] = hextab[c & 0x0f];
- if (!stralloc_catb(sa,xch,2)) temp_nomem();
- } else
- if (!stralloc_catb(sa,s + i,1)) temp_nomem();
- }
-
- return sa->len;
-}
-
-void mailfrom_xtext()
-{
- if (!xtext(&xuser,user.s,user.len)) temp_nomem();
- buffer_puts(&bo,"MAIL FROM:<");
- buffer_put(&bo,sender.s,sender.len);
- buffer_puts(&bo,"> AUTH=");
- buffer_put(&bo,xuser.s,xuser.len);
- if (flagutf8 || utf8received())
- buffer_puts(&bo," SMTPUTF8");
- if (flagsize && msize) {
- buffer_puts(&bo," SIZE=");
- buffer_puts(&bo,msgsize);
- }
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-}
-
-int mailfrom_plain()
-{
- buffer_puts(&bo,"AUTH PLAIN\r\n");
- buffer_flush(&bo);
-
- if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH PLAIN)");
-
- if (!stralloc_cats(&plain,"")) temp_nomem(); /* RFC 4616 section 2 */
- if (!stralloc_0(&plain)) temp_nomem();
- if (!stralloc_cat(&plain,&user)) temp_nomem(); /* user-id */
- if (!stralloc_0(&plain)) temp_nomem();
- if (!stralloc_cat(&plain,&pass)) temp_nomem(); /* password */
- if (b64encode(&plain,&auth)) quit("ZConnected to "," but unable to base64encode (plain)");
- buffer_put(&bo,auth.s,auth.len);
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-
- switch (smtpcode()) {
- case 235: mailfrom_xtext(); break;
- case 432: quit("DConnected to "," but password expired");
- case 534: quit("ZConnected to "," but authentication mechamism too weak (plain)");
- default: quit("ZConnected to "," but authentication was rejected (plain)");
- }
- return 0;
-}
-
-int mailfrom_login()
-{
- buffer_puts(&bo,"AUTH LOGIN\r\n");
- buffer_flush(&bo);
-
- if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)");
- if (!stralloc_copys(&auth,"")) temp_nomem();
- if (b64encode(&user,&auth)) quit("ZConnected to "," but unable to base64encode user");
-
- buffer_put(&bo,auth.s,auth.len);
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-
- if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username)");
-
- if (!stralloc_copys(&auth,"")) temp_nomem();
- if (b64encode(&pass,&auth)) quit("ZConnected to "," but unable to base64encode pass");
- buffer_put(&bo,auth.s,auth.len);
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-
- switch (smtpcode()) {
- case 235: mailfrom_xtext(); break;
- case 432: quit("DConnected to "," but password expired");
- case 534: quit("ZConnected to "," but authentication mechanism is too weak (login)");
- default: quit("ZConnected to "," but authentication was rejected (login)");
- }
- return 0;
-}
-
-int mailfrom_cram()
-{
- int j;
- unsigned char digest[16];
- unsigned char digascii[33];
-
- buffer_puts(&bo,"AUTH CRAM-MD5\r\n");
- buffer_flush(&bo);
-
- if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH CRAM-MD5)");
- if (str_chr(smtptext.s + 4,' ')) { /* Challenge */
- if (!stralloc_copys(&slop,"")) temp_nomem();
- if (!stralloc_copyb(&slop,smtptext.s + 4,smtptext.len - 5)) temp_nomem();
- if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge");
- }
-
- hmac_md5((unsigned char *)chal.s,chal.len,pass.s,pass.len,digest);
-
- for (j = 0; j < 16; j++) { /* HEX => ASCII */
- digascii[2 * j] = hextab[digest[j] >> 4];
- digascii[2 * j + 1] = hextab[digest[j] & 0x0f];
- }
- digascii[32]=0;
-
- if (!stralloc_copys(&slop,"")) temp_nomem();
- if (!stralloc_cat(&slop,&user)) temp_nomem(); /* user-id */
- if (!stralloc_cats(&slop," ")) temp_nomem();
- if (!stralloc_catb(&slop,digascii,32)) temp_nomem(); /* digest */
-
- if (!stralloc_copys(&auth,"")) temp_nomem();
- if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest");
-
- buffer_put(&bo,auth.s,auth.len);
- buffer_puts(&bo,"\r\n");
- buffer_flush(&bo);
-
- switch (smtpcode()) {
- case 235: mailfrom_xtext(); break;
- case 432: quit("DConnected to "," but password expired");
- case 534: quit("ZConnected to "," but authentication mechamism too weak (cram)");
- default: quit("ZConnected to "," but authentication was rejected (cram)");
- }
- return 0;
-}
-
-void smtp_auth()
-{
- int i;
-
- if (smtptext.len > 8)
- for (i = 4; i < smtptext.len - 5; ++i) {
- if (case_starts(smtptext.s + i,"CRAM"))
- if (mailfrom_cram() >= 0) return;
- if (case_starts(smtptext.s + i,"LOGIN"))
- if (mailfrom_login() >= 0) return;
- if (case_starts(smtptext.s + i,"PLAIN"))
- if (mailfrom_plain() >= 0) return;
- }
- err_authprot();
- mailfrom();
-}
-
-/* this file is too long ------------------------------------------- GO ON */
-
-void smtp()
-{
- int flagbother;
- int i;
-
- if (flagtls > 10 && flagtls < 20) { /* SMTPS */
- tls_init();
- tls_peercheck();
- }
-
- code = smtpcode();
- if (code >= 500) quit("DConnected to "," but sender was rejected");
- if (code == 421 || code == 450) quit("ZConnected to "," but probably greylisted"); /* RFC 6647 */
- if (code >= 400) quit("ZConnected to "," but sender was rejected");
- if (code != 220) quit("ZConnected to "," but greeting failed");
-
- smtp_greeting();
-
- if (flagtls > 0 && flagtls < 10) { /* STARTTLS */
- if (starttls_peer())
- smtp_starttls();
- else if (flagtls > 3 && flagtls != 9) {
- if (!stralloc_0(&host)) temp_nomem();
- temp_tlshost();
- }
- }
- if (user.len && pass.len) /* AUTH */
- smtp_auth();
- else
- mailfrom(); /* Mail From */
-
- code = smtpcode();
- if (code >= 500) quit("DConnected to "," but sender was rejected");
- if (code >= 400) quit("ZConnected to "," but sender was probably greylisted");
-
- flagbother = 0; /* Rcpt To */
- for (i = 0; i < reciplist.len; ++i) {
- buffer_puts(&bo,"RCPT TO:<");
- buffer_put(&bo,reciplist.sa[i].s,reciplist.sa[i].len);
- buffer_puts(&bo,">\r\n");
- buffer_flush(&bo);
-
- code = smtpcode(); /* Data */
- if (flagsize) {
- if (code == 552) quit("DConnected to "," but message size is too large");
- if (code == 452) quit("ZConnected to "," however insufficient storage space available");
- }
- if (code == 421 || code == 450) { // Postfix merde ;-)
- out("s"); outhost(); out(" sender is greylisting.\n");
- outsmtptext(); zero();
- } else if (code >= 500) {
- out("h"); outhost(); out(" does not like recipient.\n");
- outsmtptext(); zero();
- } else if (code >= 400) {
- out("s"); outhost(); out(" does not like recipient.\n");
- outsmtptext(); zero();
- } else {
- out("r"); zero();
- flagbother = 1;
- }
- }
- if (!flagbother) quit("DGiving up on ","");
-
- buffer_putsflush(&bo,"DATA\r\n");
-
- code = smtpcode();
- if (code >= 500) quit("D"," failed on DATA command");
- if (code >= 400) quit("Z"," failed on DATA command");
-
- buffer_putflush(&bo,header.s,header.len);
-
- blast();
- code = smtpcode();
- flagcritical = 0;
- if (code >= 500) quit("D"," failed after I sent the message");
- if (code >= 400) quit("Z"," failed after I sent the message");
- switch (flagtls) { // StartTLS + SMTPS
- case 100: case 110: quit("K"," TLS transmitted message accepted"); break;
- case 101: case 111: quit("K"," TLS (verified CA) transmitted message accepted"); break;
- case 102: case 112: quit("K"," TLS (validated CA+DN*) transmitted message accepted"); break;
- case 103: case 113: quit("K"," TLS (validated CA+DN) transmitted message accepted"); break;
- case 104: case 114: quit("K"," TLS (CERT pinning) transmitted message accepted"); break;
- case 105: case 115: quit("K"," TLS (TLSA EE validated) transmitted message accepted"); break;
- case 106: case 116: quit("K"," TLS (TLSA TA validated) transmitted message accepted"); break;
- case 107: case 117: quit("K"," TLS (TLSA PKIX verified) transmitted message accepted"); break;
- default: quit("K"," accepted message"); break;
- }
-}
-
-/* this file is too long -------------------------------------- qmtp client */
-
-int qmtpsend = 0;
-
-void qmtp()
-{
- unsigned long len;
- char *x;
- int i;
- int n;
- unsigned char ch;
- char num[FMT_ULONG];
- int flagallok;
-
- if (qmtpsend == 2) { /* QMTPS */
- tls_init();
- tls_peercheck();
- }
-
-/* the following code was substantially taken from serialmail's serialqmtp.c */
-
- scan_ulong(msgsize,&len);
- buffer_put(&bo,num,fmt_ulong(num,len + 1));
- buffer_put(&bo,":\n",2);
- while (len > 0) {
- n = buffer_feed(&bi);
- if (n <= 0) _exit(1); /* wise guy again */
- x = buffer_PEEK(&bi);
- buffer_put(&bo,x,n);
- buffer_SEEK(&bi,n);
- len -= n;
- }
- buffer_put(&bo,",",1);
-
- len = sender.len;
- buffer_put(&bo,num,fmt_ulong(num,len));
- buffer_put(&bo,":",1);
- buffer_put(&bo,sender.s,sender.len);
- buffer_put(&bo,",",1);
-
- len = 0;
- for (i = 0; i < reciplist.len; ++i)
- len += fmt_ulong(num,reciplist.sa[i].len) + 1 + reciplist.sa[i].len + 1;
- buffer_put(&bo,num,fmt_ulong(num,len));
- buffer_put(&bo,":",1);
- for (i = 0; i < reciplist.len; ++i) {
- buffer_put(&bo,num,fmt_ulong(num,reciplist.sa[i].len));
- buffer_put(&bo,":",1);
- buffer_put(&bo,reciplist.sa[i].s,reciplist.sa[i].len);
- buffer_put(&bo,",",1);
- }
- buffer_put(&bo,",",1);
- buffer_flush(&bo);
-
- flagallok = 1;
-
- for (i = 0; i < reciplist.len; ++i) {
- len = 0;
- for (;;) {
- get(&ch);
- if (ch == ':') break;
- if (len > QMTP_MAX) temp_proto();
- if (ch - '0' > 9) temp_proto();
- len = 10 * len + (ch - '0');
- }
- if (!len) temp_proto();
- get(&ch); --len;
- if ((ch != 'Z') && (ch != 'D') && (ch != 'K')) temp_proto();
-
- if (!stralloc_copyb(&smtptext,&ch,1)) temp_proto();
- if (flagtls == 100) {
- if (!stralloc_cats(&smtptext,"qmtps:")) temp_nomem();
- } else {
- if (!stralloc_cats(&smtptext,"qmtp:")) temp_nomem();
- }
-
- while (len > 0) {
- get(&ch);
- --len;
- }
-
- for (len = 0; len < smtptext.len; ++len) {
- ch = smtptext.s[len];
- if ((ch < 32) || (ch > 126)) smtptext.s[len] = '?';
- }
- get(&ch);
- if (ch != ',') temp_proto();
- smtptext.s[smtptext.len - 1] = '\n';
-
- if (smtptext.s[0] == 'K') out("r");
- else if (smtptext.s[0] == 'D') {
- out("h");
- flagallok = 0;
- }
- else { /* if (smtptext.s[0] == 'Z') */
- out("s");
- flagallok = 0;
- }
- if (buffer_put(&bs,smtptext.s + 1,smtptext.len - 1) == -1) temp_qmtpnoc();
- zero();
- }
- if (!flagallok) {
- out("DGiving up on "); outhost(); out("\n");
- } else {
- out("KAll received okay by "); outhost(); out("\n");
- }
- zerodie();
-}
-
-/* this file is too long -------------------------------------- common */
-
-/* host has to be canonical [A/AAAA record], box has to be quoted */
-
-void addrmangle(stralloc *saout,char *address,int *flagalias,int flagcname)
-{
- int at;
- int r = 0;
- stralloc cn = {0};
-
- *flagalias = flagcname; /* saout + flagalias are output */
- if (!flagutf8)
- flagutf8 = utf8string(address,str_len(address));
-
- at = str_rchr(address,'@');
- if (!address[at]) {
- if (!stralloc_copys(saout,address)) temp_nomem();
- return;
- }
-
- if (!stralloc_copys(&canonbox,address)) temp_nomem();
- canonbox.len = at;
- if (!quote(saout,&canonbox)) temp_nomem(); /* saout = 'inbox' name without quotes ;-) */
- if (!stralloc_cats(saout,"@")) temp_nomem();
-
- if (!stralloc_copys(&canonhost,address + at + 1)) temp_nomem();
- if (flagcname) { /* no relayhost */
- DNS_INIT
- switch ((r = dns_cname(&cn,&canonhost))) {
- case DNS_MEM: temp_nomem();
- case DNS_SOFT: temp_dnscanon();
- case DNS_HARD: ; /* alias loop, not our problem */
- default: if (r > 0) *flagalias = 0;
- }
- }
- if (!stralloc_cat(saout,&canonhost)) temp_nomem();
-}
-
-void getcontrols()
-{
- if (control_init() == -1) temp_control();
- if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
- if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
- temp_control();
- if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1)
- temp_control();
- switch (control_readfile(&smtproutes,"control/smtproutes",0)) {
- case -1: temp_control();
- case 0: if (!constmap_init(&mapsmtproutes,"",0,1)) temp_nomem(); break;
- case 1: if (!constmap_init(&mapsmtproutes,smtproutes.s,smtproutes.len,1)) temp_nomem(); break;
- }
- switch (control_readfile(&domainips,"control/domainips",0)) {
- case -1: temp_control();
- case 0: if (!constmap_init(&mapdomainips,"",0,1)) temp_nomem(); break;
- case 1: if (!constmap_init(&mapdomainips,domainips.s,domainips.len,1)) temp_nomem(); break;
- }
- switch (control_readfile(&authsenders,"control/authsenders",0)) {
- case -1: temp_control();
- case 0: if (!constmap_init(&mapauthsenders,"",0,1)) temp_nomem(); break;
- case 1: if (!constmap_init(&mapauthsenders,authsenders.s,authsenders.len,1)) temp_nomem(); break;
- }
- switch (control_readfile(&qmtproutes,"control/qmtproutes",0)) {
- case -1: temp_control();
- case 0: if (!constmap_init(&mapqmtproutes,"",0,1)) temp_nomem(); break;
- case 1: if (!constmap_init(&mapqmtproutes,qmtproutes.s,qmtproutes.len,1)) temp_nomem(); break;
- }
- switch (control_readfile(&domaincerts,"control/domaincerts",0)) {
- case -1: temp_control();
- case 0: if (!constmap_init(&mapdomaincerts,"",0,1)) temp_nomem(); break;
- case 1: if (!constmap_init(&mapdomaincerts,domaincerts.s,domaincerts.len,1)) temp_nomem(); break;
- }
- switch (control_readfile(&tlsdestinations,"control/tlsdestinations",0)) {
- case -1: temp_control();
- case 0: if (!constmap_init(&maptlsdestinations,"",0,1)) temp_nomem(); break;
- case 1: if (!constmap_init(&maptlsdestinations,tlsdestinations.s,tlsdestinations.len,1)) temp_nomem(); break;
- }
-}
-
-int main(int argc,char **argv)
-{
- static ipalloc ip = {0};
- stralloc netif = {0};
- struct stat st;
- int i, j, k;
- int p; /* reserved for port */
- int r; /* reserved for return code */
- unsigned long random;
- char **recips;
- unsigned long prefme;
- int flagallaliases;
- int flagalias;
- char *relayhost;
- char *localip;
- int ip6flag = 0;
-
- sig_pipeignore();
- if (argc < 4) perm_usage();
- if (chdir(auto_qmail) == -1) temp_chdir();
-
- getcontrols();
- if (!stralloc_copys(&host,argv[1])) temp_nomem();
-
- authsender = 0;
- relayhost = 0;
-
- addrmangle(&sender,argv[2],&flagalias,0);
-
- if (sender.len > 1) {
- i = str_chr(sender.s,'@');
- if (sender.s[i] == '@')
- if (!stralloc_copyb(&senddomain,sender.s + i + 1,sender.len - i - 1)) temp_nomem(); // un-terminated
- }
-
-/* this file is too long -------------------------------------- set domain ip + helohost */
-
- localip = 0;
-
- for (i = 0; i <= senddomain.len; ++i)
- if ((i == 0) || (senddomain.s[i] == '.'))
- if ((localip = constmap(&mapdomainips,senddomain.s + i,senddomain.len - i)))
- break;
-
- if (!localip)
- localip = constmap(&mapdomainips,"*",1); /* one for all */
-
- if (localip) {
- j = str_chr(localip,'%');
- if (localip[j] != '%') j = 0;
- k = str_chr(localip,'|');
- if (localip[k] != '|') k = 0;
- if (k) { /* helohost */
- if (!stralloc_copys(&helohost,localip + k + 1)) temp_nomem();
- if (!stralloc_0(&helohost)) temp_nomem();
- localip[k] = 0;
- }
- if (j) { /* IF index */
- localip[j] = 0;
- if (!stralloc_copys(&netif,localip + j + 1)) temp_nomem();
- if (!stralloc_0(&netif)) temp_nomem();
- }
- }
-
-/* this file is too long -------------------------------------- authsender routes */
-
- for (i = 0; i <= sender.len; ++i)
- if ((i == 0) || (i == sender.len) || (sender.s[i] == '.') || (sender.s[i] == '@'))
- if ((authsender = constmap(&mapauthsenders,sender.s + i,sender.len - i)))
- break;
-
- if (authsender && !*authsender) authsender = 0;
-
- if (authsender) {
- i = str_chr(authsender,'|');
- if (authsender[i] == '|') {
- j = str_chr(authsender + i + 1,'|');
- if (authsender[i + j + 1] == '|') {
- authsender[i] = 0;
- authsender[i + j + 1] = 0;
- if (!stralloc_copys(&user,"")) temp_nomem();
- if (!stralloc_copys(&user,authsender + i + 1)) temp_nomem();
- if (!stralloc_copys(&pass,"")) temp_nomem();
- if (!stralloc_copys(&pass,authsender + i + j + 2)) temp_nomem();
- }
- }
- p = str_chr(authsender,';');
- if (authsender[p] == ';') {
- if (authsender[p + 1] == 's') { flagsmtps = 1, p++; }
- scan_ulong(authsender + p + 1,&port);
- authsender[p] = 0;
- }
- relayhost = authsender;
- if (!stralloc_copys(&host,authsender)) temp_nomem();
- }
-
-/* this file is too long -------------------------------------- standard routes */
-
- if (!authsender) {
- if (sender.len == 0) { /* bounce routes */
- if ((relayhost = constmap(&mapqmtproutes,"!@",2))) {
- qmtpsend = 1; port = PORT_QMTP;
- } else
- relayhost = constmap(&mapsmtproutes,"!@",2);
- }
-
- if (relayhost && !*relayhost) relayhost = 0;
-
- if (!relayhost) {
- for (i = 0; i <= host.len; ++i) { /* qmtproutes */
- if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
- if ((relayhost = constmap(&mapqmtproutes,host.s + i,host.len - i))) {
- qmtpsend = 1; port = PORT_QMTP;
- break;
- } /* default smtproutes */
- if ((relayhost = constmap(&mapsmtproutes,host.s + i,host.len - i)))
- break;
- }
- }
- if (relayhost && !*relayhost) relayhost = 0;
-
- if (relayhost) { /* default smtproutes -- authenticated */
- i = str_chr(relayhost,'|');
- if (relayhost[i] == '|') {
- j = str_chr(relayhost + i + 1,'|'); // authenticate
- if (relayhost[i + j + 1] == '|') {
- relayhost[i] = 0;
- relayhost[i + j + 1] = 0;
- if (!stralloc_copys(&user,"")) temp_nomem();
- if (!stralloc_copys(&user,relayhost + i + 1)) temp_nomem();
- if (!stralloc_copys(&pass,"")) temp_nomem();
- k = str_chr(relayhost + i + j + 2,'|'); // local ip
- if (relayhost[i + j + k + 2] == '|') {
- relayhost[i + j + k + 2] = 0;
- localip = relayhost + i + j + k + 3;
- }
- if (!stralloc_copys(&pass,relayhost + i + j + 2)) temp_nomem();
- }
- }
- p = str_chr(relayhost,';');
- if (relayhost[p] == ';') {
- if (relayhost[p + 1] == 's') { flagsmtps = 1; p++; } // RFC 8314
- scan_ulong(relayhost + p + 1,&port);
- if (qmtpsend && port == PORT_QMTPS) qmtpsend = 2;
- relayhost[p] = 0;
- }
- if (!stralloc_copys(&host,relayhost)) temp_nomem();
-#ifdef IDN2
- } else {
- char *asciihost = 0;
- if (!stralloc_0(&host)) temp_nomem();
- switch (idn2_lookup_u8(host.s,(uint8_t**)&asciihost,IDN2_NFC_INPUT)) {
- case IDN2_OK: break;
- case IDN2_MALLOC: temp_nomem();
- default: perm_dns();
- }
- if (!stralloc_copys(&idnhost,asciihost)) temp_nomem();
-#endif
- }
- }
-
-/* this file is too long -------------------------------------- TLS destinations */
-
-
- flagtls = tls_destination((const stralloc) host); // host may not be 0-terminated
-
- if (flagtls > 0) {
- if (tlsdestinfo) {
- i = str_chr(tlsdestinfo,'|'); /* ca file/dir or cert fingerprint */
- if (tlsdestinfo[i] == '|') {
- tlsdestinfo[i] = 0;
- j = str_chr(tlsdestinfo + i + 1,'|'); /* cipher */
- if (tlsdestinfo[i + j + 1] == '|') {
- tlsdestinfo[i + j + 1] = 0;
- k = str_chr(tlsdestinfo + i + j + 2,'|'); /* cone domain */
- if (tlsdestinfo[i + j + k + 2] == '|') {
- tlsdestinfo[i + j + k + 2] = 0;
- if (str_diffn(tlsdestinfo + j + k + 3,canonhost.s,canonhost.len)) flagtls = 0;
- }
- p = str_chr(tlsdestinfo + i + j + 2,';'); /* verifydepth;port */
- if (tlsdestinfo[i + j + p + 2] == ';') {
- tlsdestinfo[i + j + p + 2] = 0;
- if (p > 0) scan_ulong(tlsdestinfo + i + j + 2,&verifydepth);
- if (tlsdestinfo[i + j + p + 3] == 's') { flagsmtps = 1; p++; } /* RFC 8314 */
- scan_ulong(tlsdestinfo + i + j + p + 3,&port);
- }
- }
- if (j)
- if (!stralloc_copys(&ciphers,tlsdestinfo + i + 1)) temp_nomem();
- }
-
- /* either ':[=]cafile/cadir' -or- ':;port' */
-
- if (tlsdestinfo[0] == ';')
- scan_ulong(tlsdestinfo + 1,&port);
- else
- if (!stralloc_copys(&cafile,tlsdestinfo)) temp_nomem();
- }
-
-/* cafile starts with '=' => it is a fingerprint
- cafile ends with '/' => consider it as cadir
- cafile and cadir are now 0-terminated
- ciphers are alway 0-terminated if given */
-
- if (cafile.len > 2) {
- flagverify = 1;
- if (cafile.s[cafile.len] == '/') {
- cafile.len = 0;
- flagverify = 2;
- if (!stralloc_copys(&cadir,tlsdestinfo)) temp_nomem();
- if (!stralloc_0(&cadir)) temp_nomem();
- } else {
- if (cafile.s[0] == '=') flagverify = -2;
- if (!stralloc_0(&cafile)) temp_nomem();
- }
- } else
- cafile.len = cadir.len = 0;
-
- if (ciphers.len > 4) /* otherwise garbage */
- if (!stralloc_0(&ciphers)) temp_nomem();
- else
- ciphers.len = 0;
-
- if (port == PORT_SMTPS || flagsmtps) flagtls += 10;
- if (port == PORT_QMTPS) flagtls += 20;
- }
-
- if (flagtls == 8) flagverify = -1;
- if (!flagtls && qmtpsend == 2) flagtls = 20; /* QMTPS */
-
-
-/* this file is too long -------------------------------------- Our Certs - per senddomain */
-
- if (flagtls > 0) {
- flagtlsdomain = tls_domaincerts((const stralloc) senddomain); // senddomain un-terminated
-
- if (flagtlsdomain && tlsdomaininfo) {
- i = str_chr(tlsdomaininfo,'|');
- if (tlsdomaininfo[i] == '|') {
- tlsdomaininfo[i] = 0;
- j = str_chr(tlsdomaininfo + i + 1,'|');
- if (tlsdomaininfo[i + j + 1] == '|') {
- tlsdomaininfo[i + j + 1] = 0;
- if (!stralloc_copys(&keypwd,"")) temp_nomem();
- if (!stralloc_copys(&keypwd,tlsdomaininfo + i + j + 2)) temp_nomem();
- if (!stralloc_0(&keypwd)) temp_nomem();
- }
- if (!stralloc_copys(&keyfile,tlsdomaininfo + i + 1)) temp_nomem();
- if (!stralloc_0(&keyfile)) temp_nomem();
- }
- if (!stralloc_copys(&certfile,tlsdomaininfo)) temp_nomem();
- if (!stralloc_0(&certfile)) temp_nomem();
- flagtlsdomain = 2;
- }
- }
-
-/* this file is too long -------------------------------------- work thru reciplist */
-
- if (!saa_readyplus(&reciplist,0)) temp_nomem();
- if (ipme_init() != 1) temp_oserr();
-
- flagallaliases = 1;
- recips = argv + 3;
-
- if (fstat(0,&st) == -1) quit("Z", " unable to fstat stdin");
- msize = st.st_size;
- fmt_ulong(msgsize,msize);
-
- while (*recips) {
- if (!saa_readyplus(&reciplist,1)) temp_nomem();
- reciplist.sa[reciplist.len] = sauninit;
- addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost);
- if (!flagalias) flagallaliases = 0;
- ++reciplist.len;
- ++recips;
- }
-
- random = now() + (getpid() << 16);
-#ifdef IDN2
- switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&idnhost,random)) {
-#else
- switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) {
-#endif
- case DNS_MEM: temp_nomem();
- case DNS_ERR: temp_dns();
- case DNS_COM: temp_dns();
- case DNS_SOFT: temp_dns();
-#ifdef DEFERREDBOUNCES
- default: if (!ip.len) temp_nomx();
-#else
- default: if (!ip.len) perm_nomx();
-#endif
- }
-
- prefme = 100000;
- for (i = 0; i < ip.len; ++i)
- if (ipme_is(&ip.ix[i]))
- if (ip.ix[i].pref < prefme)
- prefme = ip.ix[i].pref;
-
- if (relayhost) prefme = 300000;
- if (flagallaliases) prefme = 500000;
-
- if (localip) {
- i = str_chr(localip,':');
- if (localip[i] == ':') ip6flag = 1;
- else ip6flag = -1;
- }
-
- for (i = 0; i < ip.len; ++i) { /* MX with smallest distance */
- if (ip6flag == -1 && ip.ix[i].af == AF_INET6) continue;
- if (ip6flag == 1 && ip.ix[i].af == AF_INET) continue;
- if (ip.ix[i].pref < prefme) break;
- }
-
- if (i >= ip.len)
- perm_ambigmx();
-
- if (!stralloc_copys(&remotehost,ip.ix[i].mxh)) temp_nomem(); /* take MX hostname for TLSA */
- if (!stralloc_0(&remotehost)) temp_nomem();
-
- for (i = 0; i < ip.len; ++i) {
- if (ip.ix[i].pref < prefme) {
- if (ip6flag == -1 && ip.ix[i].af == AF_INET6) continue; /* explicit binding */
- if (ip6flag == 1 && ip.ix[i].af == AF_INET) continue;
- if (tcpto(&ip.ix[i])) continue;
-
- smtpfd = socket(ip.ix[i].af,SOCK_STREAM,0);
- if (smtpfd == -1) continue;
-
- if (localip) { /* set domain ip */
- if (!stralloc_copyb(&sendip,localip,str_len(localip))) temp_nomem();
- j = str_chr(localip,':');
- if (localip[j] == ':') {
- if (!ip6_scan(localip,ip6)) temp_noip(); /* IPv6 */
- if (byte_equal(ip.ix[i].addr.ip6.d,16,ip6)) continue;
- ifidx = socket_getifidx(netif.s);
- if (socket_bind6(smtpfd,ip6,0,ifidx) < 0) temp_osip();
- } else {
- if (!ip4_scan(localip,ip4)) temp_noip(); /* IPv4 */
- if (byte_equal(ip.ix[i].addr.ip4.d,4,ip4)) continue;
- if (socket_bind4(smtpfd,ip4,0) < 0) temp_osip();
- }
- }
-
-
- AGAIN:
- if (ip.ix[i].af == AF_INET6)
- r = timeoutconn6(smtpfd,(char *)&ip.ix[i].addr.ip6.d,(unsigned int) port,timeoutconnect,ifidx);
- else
- r = timeoutconn4(smtpfd,(char *)&ip.ix[i].addr.ip4.d,(unsigned int) port,timeoutconnect);
- if (r == 0) {
- tcpto_err(&ip.ix[i],0);
- partner = ip.ix[i];
- if (qmtpsend)
- qmtp();
- else
- smtp(); /* read qmail/THOUGHTS; section 6 */
- }
- if (flagtls == 9 && errno == EPROTO) {
- flagtls = 0; goto AGAIN;
- }
- if (errno == ETIMEDOUT || errno == ECONNREFUSED || errno == EPROTO)
- tcpto_err(&ip.ix[i],1);
- close(smtpfd);
- }
- }
- temp_noconn();
-}