xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/delivered_hdr.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
1 /*	$NetBSD: delivered_hdr.c,v 1.3 2022/10/08 16:12:45 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	delivered_hdr 3
6 /* SUMMARY
7 /*	process Delivered-To: headers
8 /* SYNOPSIS
9 /*	#include <delivered_hdr.h>
10 /*
11 /*	DELIVERED_HDR_INFO *delivered_hdr_init(stream, offset, flags)
12 /*	VSTREAM	*stream;
13 /*	off_t	offset;
14 /*	int	flags;
15 /*
16 /*	int	delivered_hdr_find(info, address)
17 /*	DELIVERED_HDR_INFO *info;
18 /*	const char *address;
19 /*
20 /*	void	delivered_hdr_free(info)
21 /*	DELIVERED_HDR_INFO *info;
22 /* DESCRIPTION
23 /*	This module processes addresses in Delivered-To: headers.
24 /*	These headers are added by some mail delivery systems, for the
25 /*	purpose of breaking mail forwarding loops. N.B. This solves
26 /*	a different problem than the Received: hop count limit. Hop
27 /*	counts are used to limit the impact of mail routing problems.
28 /*
29 /*	delivered_hdr_init() extracts Delivered-To: header addresses
30 /*	from the specified message, and returns a table with the
31 /*	result. The file seek pointer is changed.
32 /*
33 /*	delivered_hdr_find() looks up the address in the lookup table,
34 /*	and returns non-zero when the address was found. The
35 /*	address argument must be in internalized form.
36 /*
37 /*	delivered_hdr_free() releases storage that was allocated by
38 /*	delivered_hdr_init().
39 /*
40 /*	Arguments:
41 /* .IP stream
42 /*	The open queue file.
43 /* .IP offset
44 /*	Offset of the first message content record.
45 /* .IP flags
46 /*	Zero, or the bit-wise OR ot:
47 /* .RS
48 /* .IP FOLD_ADDR_USER
49 /*	Case fold the address local part.
50 /* .IP FOLD_ADDR_HOST
51 /*	Case fold the address domain part.
52 /* .IP FOLD_ADDR_ALL
53 /*	Alias for (FOLD_ADDR_USER | FOLD_ADDR_HOST).
54 /* .RE
55 /* .IP info
56 /*	Extracted Delivered-To: addresses information.
57 /* .IP address
58 /*	A recipient address, internal form.
59 /* DIAGNOSTICS
60 /*	Fatal errors: out of memory.
61 /* SEE ALSO
62 /*	mail_copy(3), producer of Delivered-To: and other headers.
63 /* LICENSE
64 /* .ad
65 /* .fi
66 /*	The Secure Mailer license must be distributed with this software.
67 /* AUTHOR(S)
68 /*	Wietse Venema
69 /*	IBM T.J. Watson Research
70 /*	P.O. Box 704
71 /*	Yorktown Heights, NY 10598, USA
72 /*
73 /*	Wietse Venema
74 /*	Google, Inc.
75 /*	111 8th Avenue
76 /*	New York, NY 10011, USA
77 /*--*/
78 
79 /* System library. */
80 
81 #include <sys_defs.h>
82 #include <unistd.h>
83 #include <string.h>
84 #include <ctype.h>
85 
86 /* Utility library. */
87 
88 #include <msg.h>
89 #include <mymalloc.h>
90 #include <htable.h>
91 #include <vstring.h>
92 #include <vstream.h>
93 #include <vstring_vstream.h>
94 #include <stringops.h>
95 
96 /* Global library. */
97 
98 #include <record.h>
99 #include <rec_type.h>
100 #include <is_header.h>
101 #include <quote_822_local.h>
102 #include <header_opts.h>
103 #include <delivered_hdr.h>
104 #include <fold_addr.h>
105 
106  /*
107   * Application-specific.
108   */
109 struct DELIVERED_HDR_INFO {
110     int     flags;
111     VSTRING *buf;
112     VSTRING *fold;
113     HTABLE *table;
114 };
115 
116 #define STR(x) vstring_str(x)
117 
118 /* delivered_hdr_init - extract delivered-to information from the message */
119 
delivered_hdr_init(VSTREAM * fp,off_t offset,int flags)120 DELIVERED_HDR_INFO *delivered_hdr_init(VSTREAM *fp, off_t offset, int flags)
121 {
122     char   *cp;
123     DELIVERED_HDR_INFO *info;
124     const HEADER_OPTS *hdr;
125     int     curr_type;
126     int     prev_type;
127 
128     /*
129      * Sanity check.
130      */
131     info = (DELIVERED_HDR_INFO *) mymalloc(sizeof(*info));
132     info->flags = flags;
133     info->buf = vstring_alloc(10);
134     info->fold = vstring_alloc(10);
135     info->table = htable_create(0);
136 
137     if (vstream_fseek(fp, offset, SEEK_SET) < 0)
138 	msg_fatal("seek queue file %s: %m", VSTREAM_PATH(fp));
139 
140     /*
141      * XXX Assume that mail_copy() produces delivered-to headers that fit in
142      * a REC_TYPE_NORM or REC_TYPE_CONT record. Lowercase the delivered-to
143      * addresses for consistency.
144      *
145      * XXX Don't get bogged down by gazillions of delivered-to headers.
146      */
147 #define DELIVERED_HDR_LIMIT	1000
148 
149     for (prev_type = REC_TYPE_NORM;
150 	 info->table->used < DELIVERED_HDR_LIMIT
151 	 && ((curr_type = rec_get(fp, info->buf, 0)) == REC_TYPE_NORM
152 	     || curr_type == REC_TYPE_CONT);
153 	 prev_type = curr_type) {
154 	if (prev_type == REC_TYPE_CONT)
155 	    continue;
156 	if (is_header(STR(info->buf))) {
157 	    if ((hdr = header_opts_find(STR(info->buf))) != 0
158 		&& hdr->type == HDR_DELIVERED_TO) {
159 		cp = STR(info->buf) + strlen(hdr->name) + 1;
160 		while (ISSPACE(*cp))
161 		    cp++;
162 		cp = fold_addr(info->fold, cp, info->flags);
163 		if (msg_verbose)
164 		    msg_info("delivered_hdr_init: %s", cp);
165 		htable_enter(info->table, cp, (void *) 0);
166 	    }
167 	} else if (ISSPACE(STR(info->buf)[0])) {
168 	    continue;
169 	} else {
170 	    break;
171 	}
172     }
173     return (info);
174 }
175 
176 /* delivered_hdr_find - look up recipient in delivered table */
177 
delivered_hdr_find(DELIVERED_HDR_INFO * info,const char * address)178 int     delivered_hdr_find(DELIVERED_HDR_INFO *info, const char *address)
179 {
180     HTABLE_INFO *ht;
181     const char *addr_key;
182 
183     /*
184      * mail_copy() uses quote_822_local() when writing the Delivered-To:
185      * header. We must therefore apply the same transformation when looking
186      * up the recipient. Lowercase the delivered-to address for consistency.
187      */
188     quote_822_local(info->buf, address);
189     addr_key = fold_addr(info->fold, STR(info->buf), info->flags);
190     ht = htable_locate(info->table, addr_key);
191     return (ht != 0);
192 }
193 
194 /* delivered_hdr_free - destructor */
195 
delivered_hdr_free(DELIVERED_HDR_INFO * info)196 void    delivered_hdr_free(DELIVERED_HDR_INFO *info)
197 {
198     vstring_free(info->buf);
199     vstring_free(info->fold);
200     htable_free(info->table, (void (*) (void *)) 0);
201     myfree((void *) info);
202 }
203 
204 #ifdef TEST
205 
206 #include <msg_vstream.h>
207 #include <mail_params.h>
208 
209 char   *var_drop_hdrs;
210 
main(int arc,char ** argv)211 int     main(int arc, char **argv)
212 {
213 
214     /*
215      * We write test records to a VSTRING, then read with delivered_hdr_init.
216      */
217     VSTRING *mem_bp;
218     VSTREAM *mem_fp;
219     DELIVERED_HDR_INFO *dp;
220     struct test_case {
221 	int     rec_type;
222 	const char *content;
223 	int     expect_find;
224     };
225     const struct test_case test_cases[] = {
226 	REC_TYPE_CONT, "Delivered-To: one", 1,
227 	REC_TYPE_NORM, "Delivered-To: two", 0,
228 	REC_TYPE_NORM, "Delivered-To: three", 1,
229 	0,
230     };
231     const struct test_case *tp;
232     int     actual_find;
233     int     errors;
234 
235     msg_vstream_init(argv[0], VSTREAM_ERR);
236 
237     var_drop_hdrs = mystrdup(DEF_DROP_HDRS);
238 
239     mem_bp = vstring_alloc(VSTREAM_BUFSIZE);
240     if ((mem_fp = vstream_memopen(mem_bp, O_WRONLY)) == 0)
241 	msg_panic("vstream_memopen O_WRONLY failed: %m");
242 
243 #define REC_PUT_LIT(fp, type, lit) rec_put((fp), (type), (lit), strlen(lit))
244 
245     for (tp = test_cases; tp->content != 0; tp++)
246 	REC_PUT_LIT(mem_fp, tp->rec_type, tp->content);
247 
248     if (vstream_fclose(mem_fp))
249 	msg_panic("vstream_fclose fail: %m");
250 
251     if ((mem_fp = vstream_memopen(mem_bp, O_RDONLY)) == 0)
252 	msg_panic("vstream_memopen O_RDONLY failed: %m");
253 
254     dp = delivered_hdr_init(mem_fp, 0, FOLD_ADDR_ALL);
255 
256     for (errors = 0, tp = test_cases; tp->content != 0; tp++) {
257 	actual_find =
258 	    delivered_hdr_find(dp, tp->content + sizeof("Delivered-To:"));
259 	msg_info("test case: %c >%s<: %s (expected: %s)",
260 		 tp->rec_type, tp->content,
261 		 actual_find == tp->expect_find ? "PASS" : "FAIL",
262 		 tp->expect_find ? "MATCH" : "NO MATCH");
263 	errors += (actual_find != tp->expect_find);;
264     }
265     exit(errors);
266 }
267 
268 #endif
269