Home | History | Annotate | Line # | Download | only in global
      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 
    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 
    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 
    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 
    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