Upstream qmail 1.01
[qmail] / qmail-remote.c
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <arpa/inet.h>
5 #include "sig.h"
6 #include "getln.h"
7 #include "stralloc.h"
8 #include "substdio.h"
9 #include "subfd.h"
10 #include "scan.h"
11 #include "case.h"
12 #include "error.h"
13 #include "auto_qmail.h"
14 #include "control.h"
15 #include "dns.h"
16 #include "alloc.h"
17 #include "quote.h"
18 #include "ip.h"
19 #include "ipalloc.h"
20 #include "ipme.h"
21 #include "gen_alloc.h"
22 #include "gen_allocdefs.h"
23 #include "str.h"
24 #include "now.h"
25 #include "exit.h"
26 #include "constmap.h"
27 #include "tcpto.h"
28 #include "timeoutconn.h"
29 #include "timeoutread.h"
30 #include "timeoutwrite.h"
31
32 #define HUGESMTPTEXT 5000
33
34 #define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */
35 unsigned long port = PORT_SMTP;
36
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};
40
41 stralloc helohost = {0};
42 stralloc routes = {0};
43 struct constmap maproutes;
44 stralloc host = {0};
45 stralloc sender = {0};
46
47 saa reciplist = {0};
48
49 struct ip_address partner;
50
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); }
54
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); } }
59
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 ");
80 outsafe(&host);
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");
84 zerodie(); }
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");
88 zerodie(); }
89
90 int timeout = 1200;
91 int timeoutconnect = 60;
92
93 void getcontrols()
94 {
95 int r;
96 if (control_init() == -1)
97 { if (errno == error_nomem) temp_nomem(); temp_control(); }
98
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(); }
103
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();
107
108 switch(control_readfile(&routes,"control/smtproutes",0))
109 {
110 case -1:
111 if (errno == error_nomem) temp_nomem(); temp_control();
112 case 0:
113 if (!constmap_init(&maproutes,"",0,1)) temp_nomem(); break;
114 case 1:
115 if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
116 }
117 }
118
119 char smtptobuf[1024];
120 char smtpfrombuf[128];
121 stralloc smtpline = {0};
122 stralloc smtptext = {0};
123
124 void outsmtptext()
125 {
126 int i;
127 if (smtptext.s) if (smtptext.len) if (smtptext.len < HUGESMTPTEXT)
128 {
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);
133 smtptext.len = 0;
134 }
135 }
136
137 unsigned long smtpcode(ss)
138 substdio *ss;
139 {
140 int match;
141 unsigned long code;
142
143 if (!stralloc_copys(&smtptext,"")) return 421;
144 do
145 {
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'))
149 {
150 smtpline.s[smtpline.len - 2] = '\n';
151 --smtpline.len;
152 }
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;
156 }
157 while (smtpline.s[3] == '-');
158
159 return code;
160 }
161
162 void outhost()
163 {
164 char x[IPFMT];
165
166 x[ip_fmt(x,&partner)] = 0;
167 out(x);
168 }
169
170 void writeerr()
171 {
172 out("ZConnected to "); outhost();
173 out(" but communications failed. (#4.4.2)\n");
174 zerodie();
175 }
176
177 void quit(ssto,ssfrom)
178 substdio *ssto;
179 substdio *ssfrom;
180 {
181 outsmtptext();
182 if (substdio_putsflush(ssto,"QUIT\r\n") != -1)
183 smtpcode(ssfrom); /* protocol design stupidity */
184 zerodie();
185 }
186
187 stralloc dataline = {0};
188
189 void blast(ssto,ssfrom)
190 substdio *ssto;
191 substdio *ssfrom;
192 {
193 int match;
194
195 for (;;)
196 {
197 if (getln(ssfrom,&dataline,&match,'\n') != 0) temp_read();
198 if (!match && !dataline.len) break;
199 if (!match) perm_partialline();
200 --dataline.len;
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();
205 }
206 if (substdio_put(ssto,".\r\n",3) == -1) writeerr();
207 if (substdio_flush(ssto) == -1) writeerr();
208 }
209
210 stralloc recip = {0};
211
212 void smtp(fd)
213 int fd;
214 {
215 substdio ssto;
216 substdio ssfrom;
217 unsigned long code;
218 int flaganyrecipok;
219 int i;
220
221 substdio_fdbuf(&ssto,timeoutwrite,TIMEOUTWRITE(timeout,fd),smtptobuf,sizeof(smtptobuf));
222 substdio_fdbuf(&ssfrom,timeoutread,TIMEOUTREAD(timeout,fd),smtpfrombuf,sizeof(smtpfrombuf));
223
224 if (smtpcode(&ssfrom) != 220)
225 {
226 out("ZConnected to "); outhost(); out(" but greeting failed.\n");
227 quit(&ssto,&ssfrom);
228 }
229
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();
234
235 if (smtpcode(&ssfrom) != 250)
236 {
237 out("ZConnected to "); outhost(); out(" but my name was rejected.\n");
238 quit(&ssto,&ssfrom);
239 }
240
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();
245
246 code = smtpcode(&ssfrom);
247 if (code >= 500)
248 {
249 out("DConnected to "); outhost(); out(" but sender was rejected.\n");
250 quit(&ssto,&ssfrom);
251 }
252 if (code >= 400)
253 {
254 out("ZConnected to "); outhost(); out(" but sender was rejected.\n");
255 quit(&ssto,&ssfrom);
256 }
257
258 flaganyrecipok = 0;
259 for (i = 0;i < reciplist.len;++i)
260 {
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();
265
266 code = smtpcode(&ssfrom);
267 if (code == 421)
268 {
269 out("ZConnected to "); outhost(); out(" but connection died.\n");
270 quit(&ssto,&ssfrom);
271 }
272 if (code >= 500)
273 {
274 out("h"); outhost(); out(" does not like recipient.\n");
275 outsmtptext(); zero();
276 }
277 else if (code >= 400)
278 {
279 out("s"); outhost(); out(" does not like recipient.\n");
280 outsmtptext(); zero();
281 }
282 else
283 {
284 out("r"); zero();
285 flaganyrecipok = 1;
286 }
287 }
288
289 if (!flaganyrecipok)
290 {
291 out("DGiving up.\n");
292 quit(&ssto,&ssfrom);
293 }
294
295 if (substdio_putsflush(&ssto,"DATA\r\n") == -1) writeerr();
296
297 code = smtpcode(&ssfrom);
298 if (code == 421)
299 {
300 out("ZConnected to "); outhost(); out(" but connection died.\n");
301 quit(&ssto,&ssfrom);
302 }
303 if (code >= 500)
304 {
305 out("D"); outhost(); out(" failed on DATA command.\n");
306 quit(&ssto,&ssfrom);
307 }
308 if (code >= 400)
309 {
310 out("Z"); outhost(); out(" failed on DATA command.\n");
311 quit(&ssto,&ssfrom);
312 }
313
314 blast(&ssto,subfdin);
315
316 code = smtpcode(&ssfrom);
317 if (code == 421)
318 {
319 out("ZConnected to "); outhost(); out(" but connection died. Possible duplicate!\n");
320 quit(&ssto,&ssfrom);
321 }
322 if (code >= 500)
323 {
324 out("D"); outhost(); out(" failed after I sent the message.\n");
325 quit(&ssto,&ssfrom);
326 }
327 if (code >= 400)
328 {
329 out("Z"); outhost(); out(" failed after I sent the message.\n");
330 quit(&ssto,&ssfrom);
331 }
332
333 out("K"); outhost(); out(" accepted message.\n");
334 quit(&ssto,&ssfrom);
335 }
336
337 stralloc canonhost = {0};
338 stralloc canonbox = {0};
339
340 void addrmangle(saout,s,flagalias,flagcname)
341 stralloc *saout; /* host has to be canonical, box has to be quoted */
342 char *s;
343 int *flagalias;
344 int flagcname;
345 {
346 int j;
347
348 *flagalias = flagcname;
349
350 j = str_rchr(s,'@');
351 if (!s[j])
352 {
353 if (!stralloc_copys(saout,s)) temp_nomem();
354 return;
355 }
356 if (!stralloc_copys(&canonbox,s)) temp_nomem();
357 canonbox.len = j;
358 if (!quote(saout,&canonbox)) temp_nomem();
359 if (!stralloc_cats(saout,"@")) temp_nomem();
360
361 if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
362 if (flagcname)
363 switch(dns_cname(&canonhost))
364 {
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 */
369 }
370
371 if (!stralloc_cat(saout,&canonhost)) temp_nomem();
372 }
373
374 void main(argc,argv)
375 int argc;
376 char **argv;
377 {
378 static ipalloc ip = {0};
379 int i;
380 unsigned long random;
381 char **recips;
382 unsigned long prefme;
383 int flagallaliases;
384 int flagalias;
385 char *relayhost;
386
387 sig_pipeignore();
388 if (argc < 4) perm_usage();
389 if (chdir(auto_qmail) == -1) temp_chdir();
390 getcontrols();
391
392
393 if (!stralloc_copys(&host,argv[1])) temp_nomem();
394
395 relayhost = 0;
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))
399 break;
400 if (relayhost && !*relayhost) relayhost = 0;
401
402 if (relayhost)
403 {
404 i = str_chr(relayhost,':');
405 if (relayhost[i])
406 {
407 scan_ulong(relayhost + i + 1,&port);
408 relayhost[i] = 0;
409 }
410 if (!stralloc_copys(&host,relayhost)) temp_nomem();
411 }
412
413
414 addrmangle(&sender,argv[2],&flagalias,!relayhost);
415
416 if (!saa_readyplus(&reciplist,0)) temp_nomem();
417 if (ipme_init() != 1) temp_oserr();
418
419 flagallaliases = 1;
420 recips = argv + 3;
421 while (*recips)
422 {
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;
427 ++reciplist.len;
428 ++recips;
429 }
430
431
432 random = now() + (getpid() << 16);
433 switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random))
434 {
435 case DNS_MEM: temp_nomem();
436 case DNS_SOFT: temp_dns();
437 case DNS_HARD: perm_dns();
438 case 1:
439 if (ip.len <= 0) temp_dns();
440 }
441
442 if (ip.len <= 0) perm_nomx();
443
444 prefme = 100000;
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;
449
450 if (relayhost) prefme = 300000;
451 if (flagallaliases) prefme = 500000;
452
453 for (i = 0;i < ip.len;++i)
454 if (ip.ix[i].pref < prefme)
455 break;
456
457 if (i >= ip.len)
458 perm_ambigmx();
459
460 for (i = 0;i < ip.len;++i) if (ip.ix[i].pref < prefme)
461 {
462 int s;
463
464 if (tcpto(&ip.ix[i].ip)) continue;
465
466 s = socket(AF_INET,SOCK_STREAM,0);
467 if (s == -1) temp_oserr();
468
469 if (timeoutconn(s,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0)
470 {
471 tcpto_err(&ip.ix[i].ip,0);
472 partner = ip.ix[i].ip;
473 smtp(s); /* does not return */
474 }
475 tcpto_err(&ip.ix[i].ip,errno == error_timeout);
476 close(s);
477 }
478
479 temp_noconn();
480 }