2 #include <sys/socket.h>
3 #include <netinet/in.h>
13 #include "auto_qmail.h"
21 #include "gen_alloc.h"
22 #include "gen_allocdefs.h"
28 #include "timeoutconn.h"
29 #include "timeoutread.h"
30 #include "timeoutwrite.h"
32 #define HUGESMTPTEXT 5000
34 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
35 unsigned long port = PORT_SMTP;
37 GEN_ALLOC_typedef(saa,stralloc,sa,len,a)
38 GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus)
39 static stralloc sauninit = {0};
41 stralloc helohost = {0};
42 stralloc routes = {0};
43 struct constmap maproutes;
45 stralloc sender = {0};
49 struct ip_address partner;
51 void out(s) char *s; { if (substdio_puts(subfdout,s) == -1) _exit(0); }
52 void zero() { if (substdio_put(subfdout,"\0",1) == -1) _exit(0); }
53 void zerodie() { zero(); substdio_flush(subfdout); _exit(0); }
55 void outsafe(sa) stralloc *sa; { int i; char ch;
56 for (i = 0;i < sa->len;++i) {
57 ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?';
58 if (substdio_put(subfdout,&ch,1) == -1) _exit(0); } }
60 void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
61 void temp_oserr() { out("Z\
62 System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
63 void temp_noconn() { out("Z\
64 Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); zerodie(); }
65 void temp_read() { out("ZUnable to read message. (#4.3.0)\n"); zerodie(); }
66 void temp_dnscanon() { out("Z\
67 CNAME lookup failed temporarily. (#4.4.3)\n"); zerodie(); }
68 void temp_dns() { out("Z\
69 Sorry, I couldn't find any host by that name. (#4.1.2)\n"); zerodie(); }
70 void temp_chdir() { out("Z\
71 Unable to switch to home directory. (#4.3.0)\n"); zerodie(); }
72 void temp_control() { out("Z\
73 Unable to read control files. (#4.3.0)\n"); zerodie(); }
74 void perm_partialline() { out("D\
75 SMTP cannot transfer messages with partial final lines. (#5.6.2)\n"); zerodie(); }
76 void perm_usage() { out("D\
77 I (qmail-remote) was invoked improperly. (#5.3.5)\n"); zerodie(); }
78 void perm_dns() { out("D\
79 Sorry, I couldn't find any host named ");
81 out(". (#5.1.2)\n"); zerodie(); }
82 void perm_nomx() { out("D\
83 Sorry, I couldn't find a mail exchanger or IP address. (#5.4.4)\n");
85 void perm_ambigmx() { out("D\
86 Sorry. Although I'm listed as a best-preference MX or A for that host,\n\
87 it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
91 int timeoutconnect = 60;
96 if (control_init() == -1)
97 { if (errno == error_nomem) temp_nomem(); temp_control(); }
99 if (control_readint(&timeout,"control/timeoutremote") == -1)
100 { if (errno == error_nomem) temp_nomem(); temp_control(); }
101 if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
102 { if (errno == error_nomem) temp_nomem(); temp_control(); }
104 r = control_rldef(&helohost,"control/helohost",1,(char *) 0);
105 if (r == -1) if (errno == error_nomem) temp_nomem();
106 if (r != 1) temp_control();
108 switch(control_readfile(&routes,"control/smtproutes",0))
111 if (errno == error_nomem) temp_nomem(); temp_control();
113 if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break;
115 if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
119 char smtptobuf[1024];
120 char smtpfrombuf[128];
121 stralloc smtpline = {0};
122 stralloc smtptext = {0};
127 if (smtptext.s) if (smtptext.len) if (smtptext.len < HUGESMTPTEXT)
129 if (substdio_puts(subfdout,"Remote host said: ") == -1) _exit(0);
130 for (i = 0;i < smtptext.len;++i)
131 if (!smtptext.s[i]) smtptext.s[i] = '?';
132 if (substdio_put(subfdout,smtptext.s,smtptext.len) == -1) _exit(0);
137 unsigned long smtpcode(ss)
143 if (!stralloc_copys(&smtptext,"")) return 421;
146 if (getln(ss,&smtpline,&match,'\n') != 0) return 421;
147 if (!match) return 421;
148 if ((smtpline.len >= 2) && (smtpline.s[smtpline.len - 2] == '\r'))
150 smtpline.s[smtpline.len - 2] = '\n';
153 if (!stralloc_cat(&smtptext,&smtpline)) return 421;
154 if (scan_nbblong(smtpline.s,smtpline.len,10,0,&code) != 3) return 421;
155 if (smtpline.len == 3) return code;
157 while (smtpline.s[3] == '-');
166 x[ip_fmt(x,&partner)] = 0;
172 out("ZConnected to "); outhost();
173 out(" but communications failed. (#4.4.2)\n");
177 void quit(ssto,ssfrom)
182 if (substdio_putsflush(ssto,"QUIT\r\n") != -1)
183 smtpcode(ssfrom); /* protocol design stupidity */
187 stralloc dataline = {0};
189 void blast(ssto,ssfrom)
197 if (getln(ssfrom,&dataline,&match,'\n') != 0) temp_read();
198 if (!match && !dataline.len) break;
199 if (!match) perm_partialline();
201 if (dataline.len && (dataline.s[0] == '.'))
202 if (substdio_put(ssto,".",1) == -1) writeerr();
203 if (substdio_put(ssto,dataline.s,dataline.len) == -1) writeerr();
204 if (substdio_put(ssto,"\r\n",2) == -1) writeerr();
206 if (substdio_put(ssto,".\r\n",3) == -1) writeerr();
207 if (substdio_flush(ssto) == -1) writeerr();
210 stralloc recip = {0};
221 substdio_fdbuf(&ssto,timeoutwrite,TIMEOUTWRITE(timeout,fd),smtptobuf,sizeof(smtptobuf));
222 substdio_fdbuf(&ssfrom,timeoutread,TIMEOUTREAD(timeout,fd),smtpfrombuf,sizeof(smtpfrombuf));
224 if (smtpcode(&ssfrom) != 220)
226 out("ZConnected to "); outhost(); out(" but greeting failed.\n");
230 if (substdio_puts(&ssto,"HELO ") == -1) writeerr();
231 if (substdio_put(&ssto,helohost.s,helohost.len) == -1) writeerr();
232 if (substdio_puts(&ssto,"\r\n") == -1) writeerr();
233 if (substdio_flush(&ssto) == -1) writeerr();
235 if (smtpcode(&ssfrom) != 250)
237 out("ZConnected to "); outhost(); out(" but my name was rejected.\n");
241 if (substdio_puts(&ssto,"MAIL FROM:<") == -1) writeerr();
242 if (substdio_put(&ssto,sender.s,sender.len) == -1) writeerr();
243 if (substdio_puts(&ssto,">\r\n") == -1) writeerr();
244 if (substdio_flush(&ssto) == -1) writeerr();
246 code = smtpcode(&ssfrom);
249 out("DConnected to "); outhost(); out(" but sender was rejected.\n");
254 out("ZConnected to "); outhost(); out(" but sender was rejected.\n");
259 for (i = 0;i < reciplist.len;++i)
261 if (substdio_puts(&ssto,"RCPT TO:<") == -1) writeerr();
262 if (substdio_put(&ssto,reciplist.sa[i].s,reciplist.sa[i].len) == -1) writeerr();
263 if (substdio_puts(&ssto,">\r\n") == -1) writeerr();
264 if (substdio_flush(&ssto) == -1) writeerr();
266 code = smtpcode(&ssfrom);
269 out("ZConnected to "); outhost(); out(" but connection died.\n");
274 out("h"); outhost(); out(" does not like recipient.\n");
275 outsmtptext(); zero();
277 else if (code >= 400)
279 out("s"); outhost(); out(" does not like recipient.\n");
280 outsmtptext(); zero();
291 out("DGiving up.\n");
295 if (substdio_putsflush(&ssto,"DATA\r\n") == -1) writeerr();
297 code = smtpcode(&ssfrom);
300 out("ZConnected to "); outhost(); out(" but connection died.\n");
305 out("D"); outhost(); out(" failed on DATA command.\n");
310 out("Z"); outhost(); out(" failed on DATA command.\n");
314 blast(&ssto,subfdin);
316 code = smtpcode(&ssfrom);
319 out("ZConnected to "); outhost(); out(" but connection died. Possible duplicate!\n");
324 out("D"); outhost(); out(" failed after I sent the message.\n");
329 out("Z"); outhost(); out(" failed after I sent the message.\n");
333 out("K"); outhost(); out(" accepted message.\n");
337 stralloc canonhost = {0};
338 stralloc canonbox = {0};
340 void addrmangle(saout,s,flagalias,flagcname)
341 stralloc *saout; /* host has to be canonical, box has to be quoted */
348 *flagalias = flagcname;
353 if (!stralloc_copys(saout,s)) temp_nomem();
356 if (!stralloc_copys(&canonbox,s)) temp_nomem();
358 if (!quote(saout,&canonbox)) temp_nomem();
359 if (!stralloc_cats(saout,"@")) temp_nomem();
361 if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
363 switch(dns_cname(&canonhost))
365 case 0: *flagalias = 0; break;
366 case DNS_MEM: temp_nomem();
367 case DNS_SOFT: temp_dnscanon();
368 case DNS_HARD: ; /* alias loop, not our problem */
371 if (!stralloc_cat(saout,&canonhost)) temp_nomem();
378 static ipalloc ip = {0};
380 unsigned long random;
382 unsigned long prefme;
388 if (argc < 4) perm_usage();
389 if (chdir(auto_qmail) == -1) temp_chdir();
393 if (!stralloc_copys(&host,argv[1])) temp_nomem();
396 for (i = 0;i <= host.len;++i)
397 if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
398 if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
400 if (relayhost && !*relayhost) relayhost = 0;
404 i = str_chr(relayhost,':');
407 scan_ulong(relayhost + i + 1,&port);
410 if (!stralloc_copys(&host,relayhost)) temp_nomem();
414 addrmangle(&sender,argv[2],&flagalias,!relayhost);
416 if (!saa_readyplus(&reciplist,0)) temp_nomem();
417 if (ipme_init() != 1) temp_oserr();
423 if (!saa_readyplus(&reciplist,1)) temp_nomem();
424 reciplist.sa[reciplist.len] = sauninit;
425 addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost);
426 if (!flagalias) flagallaliases = 0;
432 random = now() + (getpid() << 16);
433 switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random))
435 case DNS_MEM: temp_nomem();
436 case DNS_SOFT: temp_dns();
437 case DNS_HARD: perm_dns();
439 if (ip.len <= 0) temp_dns();
442 if (ip.len <= 0) perm_nomx();
445 for (i = 0;i < ip.len;++i)
446 if (ipme_is(&ip.ix[i].ip))
447 if (ip.ix[i].pref < prefme)
448 prefme = ip.ix[i].pref;
450 if (relayhost) prefme = 300000;
451 if (flagallaliases) prefme = 500000;
453 for (i = 0;i < ip.len;++i)
454 if (ip.ix[i].pref < prefme)
460 for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme)
464 if (tcpto(&ip.ix[i].ip)) continue;
466 s = socket(AF_INET,SOCK_STREAM,0);
467 if (s == -1) temp_oserr();
469 if (timeoutconn(s,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0)
471 tcpto_err(&ip.ix[i].ip,0);
472 partner = ip.ix[i].ip;
473 smtp(s); /* does not return */
475 tcpto_err(&ip.ix[i].ip,errno == error_timeout);