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