/* * 20111228 * Jan Mojzis * derived from qmail-1.03/qmail-remote.c * Public domain. */ /* TODO: ... tcpto ... CNAME handling */ #include #include #include "control.h" #include "buffer.h" #include "constmap.h" #include "stralloc.h" #include "auto_qmail.h" #include "str.h" #include "byte.h" #include "scan.h" #include "dns.h" #include "curveresolve.h" #include "pathexec.h" #include "ip4.h" #include "alloc.h" #include "forkexecreadwait.h" #include "ipalloc.h" #include "error.h" #include "fmt.h" #include "ipme.h" #include "dns_mxip.h" #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ unsigned long port = PORT_SMTP; void out(const char *s) { if (buffer_puts(buffer_1small, s) == -1) _exit(0); } void zero(void) { if (buffer_put(buffer_1small,"",1) == -1) _exit(0); } void zerodie(void) { zero(); buffer_flush(buffer_1small); _exit(0); } void temp_noconn(const char *s) { out("Z\ Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); out(s); zerodie(); } void temp_nomem(void) { out("ZOut of memory. (#4.3.0)\n"); zerodie(); } void temp_oserr(void) { out("Z\ System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); } void temp_control(void) { out("Z\ Unable to read control files. (#4.3.0)\n"); zerodie(); } void temp_chdir(void) { out("Z\ Unable to switch to home directory. (#4.3.0)\n"); zerodie(); } void perm_usage(void) { out("D\ I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); } void temp_dns(void) { out("Z\ Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); } void perm_nomx(void) { out("D\ Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n"); zerodie(); } void perm_ambigmx(void) { out("D\ Sorry. 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(); } stralloc helohost = {0}; stralloc routes = {0}; stralloc host = {0}; stralloc dnsout = {0}; stralloc fqdn = {0}; stralloc sender = {0}; struct constmap maproutes; int timeoutconnect = 60; int timeout = 1200; static char seed[128]; int flagkeydir = 0; stralloc keydir = {0}; void getcontrols(void) { 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(); flagkeydir = control_rldef(&keydir,"control/keydir",0,(char *) 0); if (flagkeydir == -1) temp_control(); if (!stralloc_0(&keydir)) temp_nomem(); switch(control_readfile(&routes,"control/smtproutes",0)) { case -1: temp_control(); case 0: if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break; case 1: if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break; } } static stralloc data = {0}; static stralloc data2 = {0}; static char ipstr[IP4_FMT]; static char strport[FMT_ULONG]; static char strtimeout[FMT_ULONG]; void child(stralloc *d1, stralloc *d2, struct ip_mx *ix, int argc, char **argv, char **envp) { int i,j; char ** run; ipstr[ip4_fmt(ipstr,ix->ip)] = 0; strport[fmt_ulong(strport, port)] = 0; strtimeout[fmt_ulong(strtimeout, timeoutconnect)] = 0; if (!pathexec_env((char *)"REMOTEIP", ipstr)) temp_nomem(); if (!pathexec_env((char *)"REMOTEHOST", ix->name)) temp_nomem(); run = (char **)alloc((argc + 20) * (sizeof (const char **))); if (!run) temp_nomem(); i = 0; if (ix->haskey) { run[i++] = "qmail-curvecpclient"; if (flagkeydir) { run[i++] = "-c"; run[i++] = keydir.s; } run[i++] = "-v"; run[i++] = ix->name; run[i++] = ix->key; run[i++] = ipstr; run[i++] = strport; run[i++] = ix->ext; run[i++] = "qmail-curvecpmessage"; run[i++] = "-c"; } else { run[i++] = "qmail-tcpclient"; run[i++] = "-T"; run[i++] = strtimeout; run[i++] = "-HDRl0"; run[i++] = ipstr; run[i++] = strport; } run[i++] = "qmail-rsmtp"; for(j = 2; j < argc; ++j){ run[i++] = argv[j]; } run[i++] = 0; forkexecreadwait(d1, d2, run); } ipalloc ip = {0}; int main(int argc, char **argv, char **envp) { char *relayhost; unsigned int i; int prefme; signal(SIGPIPE,SIG_IGN); if (argc < 4) perm_usage(); if (chdir(auto_qmail) == -1) temp_chdir(); getcontrols(); helohost.s[helohost.len] = 0; if (!pathexec_env("HELOHOST", helohost.s)) temp_nomem(); if (!stralloc_copys(&host,argv[1])) temp_nomem(); relayhost = 0; for (i = 0;i <= host.len;++i) if ((i == 0) || (i == host.len) || (host.s[i] == '.')) { relayhost = constmap(&maproutes,host.s + i,host.len - i); if (relayhost) break; } if (relayhost && !*relayhost) relayhost = 0; if (relayhost) { i = str_chr(relayhost,':'); if (relayhost[i]) { scan_ulong(relayhost + i + 1,&port); relayhost[i] = 0; } if (!stralloc_copys(&host,relayhost)) temp_nomem(); } if (ipme_init() != 1) temp_oserr(); dns_random_init(seed); if (dns_mxip(&ip,&host,relayhost) == -1) temp_dns(); if (ip.len <= 0) perm_nomx(); prefme = 100000; for (i = 0;i < ip.len;++i) if (ipme_is(ip.ix[i].ip)) if (ip.ix[i].pref < prefme) prefme = ip.ix[i].pref; if (relayhost) prefme = 300000; for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) break; if (i >= ip.len) perm_ambigmx(); if (!stralloc_copys(&data,"")) temp_nomem(); if (!stralloc_copys(&data2,"")) temp_nomem(); for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme) { /* XXX TODO: if (tcpto(ip.ix[i].ip)) continue; */ if (!stralloc_copys(&data,"")) temp_nomem(); child(&data, &data2, &ip.ix[i], argc, argv, envp); if (data.len > 0) { /* XXX TODO: tcpto_err(ip.ix[i].ip,0); */ if (buffer_put(buffer_1small, data.s, data.len) == -1) _exit(0); if (buffer_flush(buffer_1small) == -1) _exit(0); _exit(0); } } /* XXX TODO: tcpto_err(ip.ix[i].ip,errno == error_timeout); */ if (!stralloc_0(&data2)) temp_nomem(); temp_noconn(data2.s); }