1 #include "stralloc.h"
2 #include "str.h"
3 #include "env.h"
4 #include "sig.h"
5 #include "slurp.h"
6 #include "getconf.h"
7 #include "strerr.h"
8 #include "byte.h"
9 #include "case.h"
10 #include "getln.h"
11 #include "substdio.h"
12 #include "error.h"
13 #include "quote.h"
14 #include "readwrite.h"
15 #include "fmt.h"
16 #include "now.h"
17 #include "cookie.h"
18 #include "subscribe.h"
19 #include "issub.h"
20 #include "log.h"
21 
22 #define FATAL "ezmlm-return: fatal: "
die_usage()23 void die_usage() { strerr_die1x(100,"ezmlm-return: usage: ezmlm-return dir"); }
die_nomem()24 void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
die_badaddr()25 void die_badaddr()
26 {
27   strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
28 }
die_trash()29 void die_trash()
30 {
31   strerr_die1x(0,"ezmlm-return: info: trash address");
32 }
33 
34 char outbuf[1024];
35 substdio ssout;
36 char inbuf[1024];
37 substdio ssin;
38 
39 char strnum[FMT_ULONG];
40 char hash[COOKIE];
41 char hashcopy[COOKIE];
42 unsigned long cookiedate;
43 stralloc fndate = {0};
44 stralloc fndatenew = {0};
45 stralloc fnhash = {0};
46 stralloc fnhashnew = {0};
47 
48 stralloc quoted = {0};
49 char *sender;
50 
die_hashnew()51 void die_hashnew()
52 { strerr_die4sys(111,FATAL,"unable to write ",fnhashnew.s,": "); }
die_datenew()53 void die_datenew()
54 { strerr_die4sys(111,FATAL,"unable to write ",fndatenew.s,": "); }
die_msgin()55 void die_msgin()
56 { strerr_die2sys(111,FATAL,"unable to read input: "); }
57 
dowit(addr,when,bounce)58 void dowit(addr,when,bounce)
59 char *addr;
60 unsigned long when;
61 stralloc *bounce;
62 {
63   int fd;
64 
65   if (!issub(addr)) return;
66 
67   if (!stralloc_copys(&fndate,"bounce/w")) die_nomem();
68   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
69   if (!stralloc_cats(&fndate,".")) die_nomem();
70   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
71   if (!stralloc_0(&fndate)) die_nomem();
72   if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
73   fndatenew.s[7] = 'W';
74 
75   fd = open_trunc(fndatenew.s);
76   if (fd == -1) die_datenew();
77   substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
78   if (substdio_puts(&ssout,addr) == -1) die_datenew();
79   if (substdio_put(&ssout,"",1) == -1) die_datenew();
80   if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
81   if (!quote2(&quoted,sender)) die_nomem();
82   if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
83   if (substdio_puts(&ssout,">\n") == -1) die_datenew();
84   if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
85   if (substdio_flush(&ssout) == -1) die_datenew();
86   if (fsync(fd) == -1) die_datenew();
87   if (close(fd) == -1) die_datenew(); /* NFS stupidity */
88 
89   if (rename(fndatenew.s,fndate.s) == -1)
90     strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
91 }
92 
doit(addr,msgnum,when,bounce)93 void doit(addr,msgnum,when,bounce)
94 char *addr;
95 unsigned long msgnum;
96 unsigned long when;
97 stralloc *bounce;
98 {
99   int fd;
100   int fdnew;
101 
102   if (!issub(addr)) return;
103 
104   if (!stralloc_copys(&fndate,"bounce/d")) die_nomem();
105   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
106   if (!stralloc_cats(&fndate,".")) die_nomem();
107   if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
108   if (!stralloc_0(&fndate)) die_nomem();
109   if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
110   fndatenew.s[7] = 'D';
111 
112   fd = open_trunc(fndatenew.s);
113   if (fd == -1) die_datenew();
114   substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
115   if (substdio_puts(&ssout,addr) == -1) die_datenew();
116   if (substdio_put(&ssout,"",1) == -1) die_datenew();
117   if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
118   if (!quote2(&quoted,sender)) die_nomem();
119   if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
120   if (substdio_puts(&ssout,">\n") == -1) die_datenew();
121   if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
122   if (substdio_flush(&ssout) == -1) die_datenew();
123   if (fsync(fd) == -1) die_datenew();
124   if (close(fd) == -1) die_datenew(); /* NFS stupidity */
125 
126   cookie(hash,"",0,"",addr,"");
127   if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
128   if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
129   if (!stralloc_0(&fnhash)) die_nomem();
130   if (!stralloc_copy(&fnhashnew,&fnhash)) die_nomem();
131   fnhashnew.s[7] = 'H';
132 
133   fdnew = open_trunc(fnhashnew.s);
134   if (fdnew == -1) die_hashnew();
135   substdio_fdbuf(&ssout,write,fdnew,outbuf,sizeof(outbuf));
136 
137   fd = open_read(fnhash.s);
138   if (fd == -1) {
139     if (errno != error_noent)
140       strerr_die4sys(111,FATAL,"unable to read ",fnhash.s,": ");
141     if (rename(fndatenew.s,fndate.s) == -1)
142       strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
143   }
144   else {
145     substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
146     switch(substdio_copy(&ssout,&ssin)) {
147       case -2: die_msgin();
148       case -3: die_hashnew();
149     }
150     close(fd);
151     if (unlink(fndatenew.s) == -1)
152       strerr_die4sys(111,FATAL,"unable to unlink ",fndatenew.s,": ");
153   }
154   if (substdio_puts(&ssout,"   ") == -1) die_hashnew();
155   if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1) die_hashnew();
156   if (substdio_puts(&ssout,"\n") == -1) die_hashnew();
157   if (substdio_flush(&ssout) == -1) die_hashnew();
158   if (fsync(fdnew) == -1) die_hashnew();
159   if (close(fdnew) == -1) die_hashnew(); /* NFS stupidity */
160 
161   if (rename(fnhashnew.s,fnhash.s) == -1)
162     strerr_die6sys(111,FATAL,"unable to rename ",fnhashnew.s," to ",fnhash.s,": ");
163 }
164 
165 stralloc bounce = {0};
166 stralloc line = {0};
167 stralloc header = {0};
168 stralloc intro = {0};
169 stralloc failure = {0};
170 stralloc paragraph = {0};
171 int flaghaveheader;
172 int flaghaveintro;
173 
174 stralloc key = {0};
175 stralloc inhost = {0};
176 stralloc outhost = {0};
177 stralloc inlocal = {0};
178 stralloc outlocal = {0};
179 
180 char msginbuf[1024];
181 substdio ssmsgin;
182 
main(argc,argv)183 void main(argc,argv)
184 int argc;
185 char **argv;
186 {
187   char *dir;
188   char *host;
189   char *local;
190   char *action;
191   unsigned long msgnum;
192   unsigned long cookiedate;
193   unsigned long when;
194   int match;
195   int i;
196   int fdlock;
197 
198   umask(022);
199   sig_pipeignore();
200   when = (unsigned long) now();
201 
202   dir = argv[1];
203   if (!dir) die_usage();
204 
205   sender = env_get("SENDER");
206   if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
207   local = env_get("LOCAL");
208   if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
209   host = env_get("HOST");
210   if (!host) strerr_die2x(100,FATAL,"HOST not set");
211 
212   if (chdir(dir) == -1)
213     strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
214 
215   switch(slurp("key",&key,32)) {
216     case -1:
217       strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
218     case 0:
219       strerr_die3x(100,FATAL,dir,"/key does not exist");
220   }
221   getconf_line(&inhost,"inhost",1,FATAL,dir);
222   getconf_line(&inlocal,"inlocal",1,FATAL,dir);
223   getconf_line(&outhost,"outhost",1,FATAL,dir);
224   getconf_line(&outlocal,"outlocal",1,FATAL,dir);
225 
226   if (inhost.len != str_len(host)) die_badaddr();
227   if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
228   if (inlocal.len > str_len(local)) die_badaddr();
229   if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
230 
231   action = local + inlocal.len;
232 
233   if (!str_start(action,"-return-")) die_badaddr();
234   action += 8;
235 
236   if (!*action) die_trash();
237 
238   if (str_start(action,"probe-")) {
239     action += 6;
240     action += scan_ulong(action,&cookiedate);
241     if (now() - cookiedate > 3000000) die_trash();
242     if (*action++ != '.') die_trash();
243     i = str_chr(action,'-');
244     if (i != COOKIE) die_trash();
245     byte_copy(hashcopy,COOKIE,action);
246     action += COOKIE;
247     if (*action++ != '-') die_trash();
248     i = str_rchr(action,'=');
249     if (!stralloc_copyb(&line,action,i)) die_nomem();
250     if (action[i]) {
251       if (!stralloc_cats(&line,"@")) die_nomem();
252       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
253     }
254     if (!stralloc_0(&line)) die_nomem();
255     strnum[fmt_ulong(strnum,cookiedate)] = 0;
256     cookie(hash,key.s,key.len,strnum,line.s,"P");
257     if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
258 
259     if (subscribe(line.s,0) == 1) log("-probe",line.s);
260     _exit(0);
261   }
262 
263   fdlock = open_append("lockbounce");
264   if (fdlock == -1)
265     strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
266   if (lock_ex(fdlock) == -1)
267     strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
268 
269   if (str_start(action,"warn-")) {
270     action += 5;
271     action += scan_ulong(action,&cookiedate);
272     if (now() - cookiedate > 3000000) die_trash();
273     if (*action++ != '.') die_trash();
274     i = str_chr(action,'-');
275     if (i != COOKIE) die_trash();
276     byte_copy(hashcopy,COOKIE,action);
277     action += COOKIE;
278     if (*action++ != '-') die_trash();
279     i = str_rchr(action,'=');
280     if (!stralloc_copyb(&line,action,i)) die_nomem();
281     if (action[i]) {
282       if (!stralloc_cats(&line,"@")) die_nomem();
283       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
284     }
285     if (!stralloc_0(&line)) die_nomem();
286     strnum[fmt_ulong(strnum,cookiedate)] = 0;
287     cookie(hash,key.s,key.len,strnum,line.s,"W");
288     if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
289 
290     if (slurpclose(0,&bounce,1024) == -1) die_msgin();
291     dowit(line.s,when,&bounce);
292     _exit(0);
293   }
294 
295   action += scan_ulong(action,&msgnum);
296   if (*action != '-') die_badaddr();
297   ++action;
298 
299   if (*action) {
300     if (slurpclose(0,&bounce,1024) == -1) die_msgin();
301 
302     i = str_rchr(action,'=');
303     if (!stralloc_copyb(&line,action,i)) die_nomem();
304     if (action[i]) {
305       if (!stralloc_cats(&line,"@")) die_nomem();
306       if (!stralloc_cats(&line,action + i + 1)) die_nomem();
307     }
308     if (!stralloc_0(&line)) die_nomem();
309     doit(line.s,msgnum,when,&bounce);
310     _exit(0);
311   }
312 
313   /* pre-VERP bounce, in QSBMF format */
314 
315   substdio_fdbuf(&ssmsgin,read,0,msginbuf,sizeof(msginbuf));
316 
317   flaghaveheader = 0;
318   flaghaveintro = 0;
319 
320   for (;;) {
321     if (!stralloc_copys(&paragraph,"")) die_nomem();
322     for (;;) {
323       if (getln(&ssmsgin,&line,&match,'\n') == -1) die_msgin();
324       if (!match) die_trash();
325       if (!stralloc_cat(&paragraph,&line)) die_nomem();
326       if (line.len <= 1) break;
327     }
328 
329     if (!flaghaveheader) {
330       if (!stralloc_copy(&header,&paragraph)) die_nomem();
331       flaghaveheader = 1;
332       continue;
333     }
334 
335     if (!flaghaveintro) {
336       if (paragraph.len < 15) die_trash();
337       if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
338       if (!stralloc_copy(&intro,&paragraph)) die_nomem();
339       flaghaveintro = 1;
340       continue;
341     }
342 
343     if (paragraph.s[0] == '-')
344       break;
345 
346     if (paragraph.s[0] == '<') {
347       if (!stralloc_copy(&failure,&paragraph)) die_nomem();
348 
349       if (!stralloc_copy(&bounce,&header)) die_nomem();
350       if (!stralloc_cat(&bounce,&intro)) die_nomem();
351       if (!stralloc_cat(&bounce,&failure)) die_nomem();
352 
353       i = byte_chr(failure.s,failure.len,'\n');
354       if (i < 3) die_trash();
355 
356       if (!stralloc_copyb(&line,failure.s + 1,i - 3)) die_nomem();
357       if (byte_chr(line.s,line.len,'\0') == line.len) {
358         if (!stralloc_0(&line)) die_nomem();
359         doit(line.s,msgnum,when,&bounce);
360       }
361     }
362   }
363 
364   _exit(0);
365 }
366