47#define PORT_SMTPS "465"
48#define SMTP_TIMEOUT 1200
52#define BASE64MESSAGE "content-transfer-encoding: base64"
77 if (r == -1)
if (errno == ETIMEDOUT)
die_alarm();
92void out(
char *s) { buffer_puts(&
bo,s); }
109 if (len < 1)
return r;
111 if (arg[len-1] ==
' ') len--;
112 if (len < 1)
return r;
114 at = byte_rchr(arg,len,
'@');
116 if (!stralloc_copyb(&
sa,arg + at + 1,len - at - 1))
die_nomem();
120 random =
now() + (getpid() << 16);
128 case DNS_ERR:
out(
"451 DNS temporary failure (#4.3.0)\r\n");
return -1;
130 default:
if (
ia.len)
return 0;
138static stralloc greeting = {0};
143 buffer_put(&
bo,greeting.s,greeting.len);
147 out(
"214 s/qmail home page: https://www.fehcom.de/sqmail.html\r\n");
222static struct cdb cdbm;
223static struct cdb cdbl;
275static stralloc spfbounce = {0};
285 if (
control_rldef(&greeting,
"control/smtpgreeting",1,(
char *) 0) != 1)
306 x = env_get(
"DATABYTES");
307 if (x) { scan_ulong(x,&u);
databytes = u; }
314 if (byte_equal(
remoteip,7,V4MAPPREFIX))
318 local = env_get(
"TCP6LOCALHOST");
339 x = env_get(
"MAXRECIPIENTS");
351 x = env_get(
"TARPITCOUNT");
353 x = env_get(
"TARPITDELAY");
356 x = env_get(
"POSTGREY");
358 if (case_diffs(x,
"-")) {
370 fdmav = open_read(
"control/mailfromrules.cdb");
383 fdbmt = open_read(
"control/badmimetypes.cdb");
403 fdblt = open_read(
"control/badloadertypes.cdb");
409 base64 = env_get(
"BASE64");
410 qhpsi = env_get(
"QHPSI");
411 auth = env_get(
"SMTPAUTH");
446 if (x) { scan_ulong(x,&u);
flagspf = u; }
459 x = env_get(
"BARELF");
471 if (!env_put(
"AUTHPROTOCOL",method))
die_nomem();
473 if (!env_unset(
"TCPREMOTEINFO"))
die_read();
475 if (!env_unset(
"TCP6REMOTEINFO"))
die_read();
486 cipher = env_get(
"SSL_CIPHER");
488 cipherperm = env_get(
"SSL_CIPHER_ALGKEYSIZE");
490 cipherused = env_get(
"SSL_CIPHER_USEKEYSIZE");
492 clientdn = env_get(
"SSL_CLIENT_S_DN");
517 clientcn = env_get(
"SSL_CLIENT_S_DN_CN");
519 dnemail = env_get(
"SSL_CLIENT_S_DN_Email");
550 struct ip4_address ip4s;
551 struct ip6_address ip6s;
556 i = str_chr(arg,
'<');
563 if (*arg ==
'@')
while (*arg)
if (*arg++ ==
':')
break;
568 for (i = 0; ch = arg[i]; ++i) {
574 if (!flagquoted && (ch == terminator))
break;
576 case '\\': flagesc = 1;
break;
577 case '"': flagquoted = !flagquoted;
break;
588 if (
addr.s[i + 1] ==
'[') {
589 if (byte_rchr(
addr.s + i + 2,
addr.len - i - 2,
':') < str_len(
addr.s)) {
590 if (!
addr.s[i + 1 + ip6_scanbracket(
addr.s + i + 1,(
char *)&ip6s.d)])
596 if (!
addr.s[i + 1 + ip4_scanbracket(
addr.s + i + 1,(
char *)&ip4s.d)])
606 if (
addr.len > 900)
return 0;
628 subvalue =
badhelo.s[i] !=
'!';
671 if (at && !case_diffs(
remotehost,
"unknown")) {
681 else if (at && rlen >=
mailfrom.len - at - 1) {
700 }
while (
j > 0 && rlen -
j > 0);
713 for (
j = 0;
j <
bmf.len; ++
j) {
715 subvalue =
bmf.s[i] !=
'!';
737 int at = byte_rchr(
addr.s,
addr.len,
'@');
752 for (
j = 0;
j <
brt.len; ++
j)
754 subvalue =
brt.s[i] !=
'!';
791 if (mflen < 1 )
return 0;
794 if (!case_diffs(
dnemail,mf))
return 2;
805 at = byte_rchr(mf,mflen,
'@');
807 if (!case_diffb(
localmfcheck,mflen - at - 1,mf + at + 1))
return 2;
828 if (!env_put(
"SPFRESULT",
"pass"))
die_nomem();
835 if (!env_put(
"SPFRESULT",
"error"))
die_nomem();
837 out(
"451 SPF lookup failure (#4.3.0)\r\n");
840 if (!env_put(
"SPFRESULT",
"none"))
die_nomem();
844 if (!env_put(
"SPFRESULT",
"unknown"))
die_nomem();
848 if (!env_put(
"SPFRESULT",
"neutral"))
die_nomem();
852 if (!env_put(
"SPFRESULT",
"softfail"))
die_nomem();
856 if (!env_put(
"SPFRESULT",
"fail"))
die_nomem();
870 unsigned long sizebytes = 0;
881 if (case_starts(arg,
"<>")) {
886 if (case_starts(arg,
"+3D")) {
887 arg = arg + 2; len = len - 2;
890 if (case_starts(arg,
"+2B")) {
891 arg = arg + 2; len = len - 2;
904 if (!env_unset(
"TCPREMOTEINFO"))
die_read();
906 if (!env_unset(
"TCP6REMOTEINFO"))
die_read();
915 if ((len = str_len(arg))) {
919 if (*arg ==
' ' || *arg ==
'\0' ) {
942 char size[FMT_ULONG];
945 out(
"250-PIPELINING\r\n250-8BITMIME\r\n");
950 case 1:
case 11:
out(
"250-AUTH LOGIN PLAIN\r\n");
break;
951 case 2:
case 12:
out(
"250-AUTH CRAM-MD5\r\n");
break;
952 case 3:
case 13:
out(
"250-AUTH LOGIN PLAIN CRAM-MD5\r\n");
break;
968 out(
"250 flushed\r\n");
975 out(
"220 Ready to start TLS (#5.7.0)\r\n");
1039 switch (child = fork()) {
1043 execv(*postgrey_scannerarg,postgrey_scannerarg);
1047 wait_pid(&wstat,child);
1048 if (wait_crashed(wstat))
return err_postgl();
1050 switch (wait_exitcode(wstat)) {
1117 if (!stralloc_0(&spfbounce))
die_nomem();
1190 case 1: rcptok =
"Recipients_Cdb";
break;
1191 case 2: rcptok =
"Recipients_Pam";
break;
1192 case 3: rcptok =
"Recipients_Users";
break;
1193 case 4: rcptok =
"Recipients_Wild";
break;
1194 default: rcptok =
"Rcpthosts_Rcptto";
break;
1223static void queue_put(
char *ch)
1235 if (*(
line.s + 1) ==
'C' || *(
line.s + 1) ==
'c')
1246 cdb_free(&cdbm); close(fdbmt);
1262 cdb_free(&cdbl); close(fdbmt);
1272 if (*(
line.s + i) ==
' ' || *(
line.s + i) ==
'\t') {
1309 pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
1311 buffer_get(&
bi,&ch,1);
1314 if (seencr == 0) { buffer_seek(&
bi,-1); ch =
'\r'; }
1316 if (ch ==
'\r') seencr = 1;
else seencr = 0;
1320 if (ch !=
"delivered"[pos])
if (ch !=
"DELIVERED"[pos]) flagmaybez = 0;
1321 if (flagmaybez)
if (pos == 8) ++*hops;
1323 if (ch !=
"received"[pos])
if (ch !=
"RECEIVED"[pos]) flagmaybex = 0;
1324 if (flagmaybex)
if (pos == 7) ++*hops;
1325 if (pos < 2)
if (ch !=
"\r\n"[pos]) flagmaybey = 0;
1326 if (flagmaybey)
if (pos == 1) flaginheader = 0;
1329 if (ch ==
'\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
1334 if (ch ==
'\r') { state = 4;
continue; }
1338 if (ch ==
'.') { state = 2;
continue; }
1339 if (ch ==
'\r') { state = 4;
continue; }
1344 if (ch ==
'\r') { state = 3;
continue; }
1348 if (ch ==
'\n')
return;
1351 if (ch ==
'\r') { state = 4;
continue; }
1355 if (ch ==
'\n') { state = 1;
break; }
1356 if (ch !=
'\r') { queue_put(
"\r"); state = 0; }
1396 out(
"354 go ahead\r\n");
1412 if (hops) {
out(
"554 too many hops, this message is looping (#5.4.6)\r\n");
return; }
1445 if (*qqx ==
'D')
out(
"554 ");
else out(
"451 ");
1453static stralloc authin = {0};
1454static stralloc
pass = {0};
1455static stralloc resp = {0};
1456static stralloc
chal = {0};
1457static stralloc
slop = {0};
1469 if (!stralloc_readyplus(&authin,1))
die_nomem();
1470 i = buffer_get(&
bi,authin.s + authin.len,1);
1472 if (authin.s[authin.len] ==
'\n')
break;
1476 if (authin.len > 0)
if (authin.s[authin.len - 1] ==
'\r') --authin.len;
1477 authin.s[authin.len] = 0;
1478 if (*authin.s ==
'*' && *(authin.s + 1) == 0)
return err_authabort();
1494 if (pipe(pi) == -1)
return err_pipe();
1495 switch (child = fork()) {
1518 if (wait_pid(&wstat,child) == -1)
return err_child();
1519 if (wait_crashed(wstat))
return err_child();
1520 if (wait_exitcode(wstat)) { sleep(
AUTHSLEEP);
return 1; }
1533 out(
"334 VXNlcm5hbWU6\r\n");
flush();
1539 out(
"334 UGFzc3dvcmQ6\r\n");
flush();
1562 if (r == -1 || !stralloc_0(&resp))
die_nomem();
1563 while (resp.s[
id])
id++;
1565 if (resp.len >
id + 1)
1567 if (resp.len >
id +
user.len + 2)
1581 s += fmt_uint(s,getpid());
1583 s += fmt_ulong(s,(
unsigned long)
now());
1600 if (r == -1 || !stralloc_0(&resp))
die_nomem();
1602 i = str_rchr(resp.s,
' ');
1604 while (*s ==
' ') ++s;
1657 i = str_chr(cmd,
' ');
1659 while (*arg ==
' ') ++arg;
1681 out(
"235 ok, go ahead (#2.0.0)\r\n");
1709int main(
int argc,
char *
const *argv)
int b64encode(stralloc *in, stralloc *out)
int b64decode(const unsigned char *in, int l, stralloc *out)
int constmap_init(struct constmap *cm, char *s, int len, int flagcolon)
int control_readint(unsigned long *i, char *fn)
int control_readline(stralloc *sa, 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)
int ipme_is6(struct ip6_address *)
int ipme_is4(struct ip4_address *)
int mfrules(int, char *, char *, char *, char *)
ssize_t saferead(int fd, char *buf, int len)
char authbuf[BUFSIZE_AUTH]
int mailfrom_size(char *arg)
void auth_info(char *method)
int auth_login(char *arg)
int addrallowed(char *arg)
void smtp_ehlo(char *arg)
void smtp_helo(char *arg)
int auth_plain(char *arg)
void acceptmessage(unsigned long qp)
void mailfrom_auth(char *arg, int len)
void smtp_mail(char *arg)
void smtp_rcpt(char *arg)
char outbuf[BUFSIZE_LINE]
char unique[FMT_ULONG+FMT_ULONG+3]
struct commands smtpcommands[]
ssize_t safewrite(int fd, char *buf, int len)
void mailfrom_parms(char *arg)
void smtp_greet(char *code)
char accept_buf[FMT_ULONG]
struct authcmd authcmds[]
unsigned long bytestooverflow
int dnsq(char *arg, char type)
void smtp_auth(char *arg)
void qmail_from(struct qmail *, char *)
void qmail_put(struct qmail *, char *, int)
char * qmail_close(struct qmail *)
unsigned long qmail_qp(struct qmail *)
int qmail_open(struct qmail *)
void qmail_fail(struct qmail *)
int rcpthosts(char *, int)
void spfheader(struct qmail *, char *, char *, char *, char *, char *)
int recipients(char *, int)
int recipients_init(void)
void err_authreq(char *, char *, char *, char *, char *)
void err_mav(char *, char *, char *, char *, char *, char *, char *)
void err_brt(char *, char *, char *, char *, char *, char *, char *)
void err_rcpts(char *, char *, char *, char *, char *, char *, char *)
void postgrey(char *, char *, char *, char *, char *, char *, char *)
void err_authinvalid(char *, char *, char *, char *, char *)
void err_recipient(char *, char *, char *, char *, char *, char *, char *)
void die_recipients(void)
void err_authfail(char *, char *, char *, char *, char *, char *, char *)
void err_size(char *, char *, char *, char *, char *, char *, char *)
void err_bmf(char *, char *, char *, char *, char *, char *, char *, char *)
void smtp_loga(char *, char *, char *, char *, char *, char *, char *, char *, char *)
void err_authsetup(char *, char *, char *, char *, char *)
void err_mfdns(char *, char *, char *, char *, char *, char *, char *)
void err_helo(char *, char *, char *, char *, char *, char *, char *, char *)
void err_spf(char *, char *, char *, char *, char *, char *, char *, char *)
void smtp_logg(char *, char *, char *, char *, char *, char *, char *)
void err_tlsreq(char *, char *, char *, char *, char *)
void err_data(char *, char *, char *, char *, char *, char *, char *, char *)
void smtp_logr(char *, char *, char *, char *, char *, char *, char *, char *)
void err_nogateway(char *, char *, char *, char *, char *, char *, char *)
int spf_parse(stralloc *, char *, char *)
spf_parse parses the substructure of the SPF record and calls spf_macros
int spf_query(const char *, const char *, const char *, const char *, const int)
spf_query prepares the SPF TXT record query
int wildmat(char *, char *)