1 #include <bglibs/systime.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <bglibs/msg.h>
6 #include "mailfront.h"
7 
8 static str received;
9 static str fixup_host;
10 static str fixup_ip;
11 
12 static const char* linkproto;
13 static const char* local_host;
14 static str local_ip;
15 static const char* remote_host;
16 static str remote_ip;
17 
date_string(void)18 static const char* date_string(void)
19 {
20   static char datebuf[64];
21   time_t now = time(0);
22   struct tm* tm = gmtime(&now);
23   strftime(datebuf, sizeof datebuf - 1, "%d %b %Y %H:%M:%S -0000", tm);
24   return datebuf;
25 }
26 
str_catfromby(str * s,const char * helo_domain,const char * host,const str * ip)27 static int str_catfromby(str* s, const char* helo_domain,
28 			 const char* host, const str* ip)
29 {
30   if (ip->len == 0)
31     ip = 0;
32   if (helo_domain == 0)
33     helo_domain = (host != 0) ? host : (ip != 0) ? ip->s : UNKNOWN;
34   if (!str_cats(s, helo_domain)) return 0;
35   if (host != 0 || ip != 0) {
36     if (!str_cats(s, " (")) return 0;
37     if (host != 0) {
38       if (!str_cats(s, host)) return 0;
39       if (ip != 0)
40 	if (!str_catc(s, ' ')) return 0;
41     }
42     if (ip != 0)
43       if (!str_catc(s, '[') ||
44 	  !str_cat(s, ip) ||
45 	  !str_catc(s, ']'))
46 	return 0;
47     if (!str_catc(s, ')')) return 0;
48   }
49   return 1;
50 }
51 
fixup_received(str * s)52 static int fixup_received(str* s)
53 {
54   if (local_host &&
55       local_ip.len > 0 &&
56       fixup_host.len > 0 &&
57       fixup_ip.len > 0 &&
58       (strcasecmp(local_host, fixup_host.s) != 0 ||
59        strcasecmp(local_ip.s, fixup_ip.s) != 0)) {
60     if (!str_cat3s(s, "Received: from ", local_host, " (")) return 0;
61     if (!str_cat4s(s, local_host, " [", local_ip.s, "])\n"
62 		   "  by ")) return 0;
63     if (!str_cat(s, &fixup_host)) return 0;
64     if (!str_cats(s, " ([")) return 0;
65     if (!str_cat(s, &fixup_ip)) return 0;
66     if (!str_cat3s(s, "]); ", date_string(), "\n")) return 0;
67   }
68   return 1;
69 }
70 
add_header_add(str * s)71 static int add_header_add(str* s)
72 {
73   const char* add = session_getenv("HEADER_ADD");
74   if (add != 0) {
75     if (!str_cats(s, add)) return 0;
76     if (!str_catc(s, '\n')) return 0;
77   }
78   return 1;
79 }
80 
build_received(str * s)81 static int build_received(str* s)
82 {
83   if (!str_cats(s, "Received: from ")) return 0;
84   if (!str_catfromby(s, session_getstr("helo_domain"),
85 		     remote_host, &remote_ip))
86     return 0;
87   if (!str_cats(s, "\n  by ")) return 0;
88   if (!str_catfromby(s, local_host, 0, &local_ip)) return 0;
89   if (!str_cat4s(s, "\n  with ", session_protocol(),
90 		 " via ", linkproto))
91     return 0;
92   if (!str_cat3s(s, "; ", date_string(), "\n")) return 0;
93   return 1;
94 }
95 
str_copyip(str * s,const char * ip,int is_ipv6)96 static int str_copyip(str* s, const char* ip, int is_ipv6)
97 {
98   s->len = 0;
99   if (ip != 0) {
100     if (is_ipv6
101 	&& !str_copys(s, "IPv6:"))
102       return 0;
103     return str_cats(s, ip);
104   }
105   return 1;
106 }
107 
init(void)108 static const response* init(void)
109 {
110   const char* tmp;
111   int is_ipv6;
112 
113   linkproto = getprotoenv(0);
114   is_ipv6 = linkproto != 0 && strcasecmp(linkproto, "TCP6") == 0;
115   if (!str_copyip(&local_ip, getprotoenv("LOCALIP"), is_ipv6)) return &resp_oom;
116   if (!str_copyip(&remote_ip, getprotoenv("REMOTEIP"), is_ipv6)) return &resp_oom;
117   local_host = getprotoenv("LOCALHOST");
118   remote_host = getprotoenv("REMOTEHOST");
119 
120   if ((tmp = getenv("FIXUP_RECEIVED_HOST")) != 0) {
121     if (!str_copys(&fixup_host, tmp)) return &resp_oom;
122     str_strip(&fixup_host);
123   }
124   if ((tmp = getenv("FIXUP_RECEIVED_IP")) != 0) {
125     if (!str_copys(&fixup_ip, tmp)) return &resp_oom;
126     str_strip(&fixup_ip);
127   }
128 
129   return 0;
130 }
131 
data_start(int fd)132 static const response* data_start(int fd)
133 {
134   int authenticated = session_getnum("authenticated", 0) || session_getenv("RELAYCLIENT") != NULL;
135   received.len = 0;
136   if (!authenticated || session_getenvu_dflt("AUTH_ADD_RECEIVED", 1) != 0)
137     if (!fixup_received(&received) ||
138         !add_header_add(&received) ||
139         !build_received(&received))
140       return &resp_internal;
141   return (received.len > 0) ? backend_data_block(received.s, received.len) : NULL;
142   (void)fd;
143 }
144 
145 struct plugin plugin = {
146   .version = PLUGIN_VERSION,
147   .init = init,
148   .data_start = data_start,
149 };
150