1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of either:
4 *
5 * a) The GNU Lesser General Public License as published by the Free
6 * Software Foundation; either version 2.1, or (at your option) any
7 * later version,
8 *
9 * OR
10 *
11 * b) The two-clause BSD license.
12 *
13 * These licenses can be found with the distribution in the file LICENSES
14 */
15
16 #include "spf_sys_config.h"
17
18 #ifdef STDC_HEADERS
19 # include <stdio.h> /* stdin / stdout */
20 # include <stdlib.h> /* malloc / free */
21 #endif
22
23 #ifdef HAVE_STRING_H
24 # include <string.h> /* strstr / strdup */
25 #else
26 # ifdef HAVE_STRINGS_H
27 # include <strings.h> /* strstr / strdup */
28 # endif
29 #endif
30
31
32 #include "spf.h"
33 #include "spf_dns.h"
34 #include "spf_request.h"
35 #include "spf_internal.h"
36
37 #define SPF_FREE(x) \
38 do { if (x) free(x); (x) = NULL; } while(0)
39
40 SPF_request_t *
SPF_request_new(SPF_server_t * spf_server)41 SPF_request_new(SPF_server_t *spf_server)
42 {
43 SPF_request_t *sr;
44
45 sr = (SPF_request_t *)malloc(sizeof(SPF_request_t));
46 if (! sr)
47 return sr;
48 memset(sr, 0, sizeof(SPF_request_t));
49
50 sr->spf_server = spf_server;
51 sr->client_ver = AF_UNSPEC;
52 sr->ipv4.s_addr = htonl(INADDR_ANY);
53 sr->ipv6 = in6addr_any;
54
55 return sr;
56 }
57
58 void
SPF_request_free(SPF_request_t * sr)59 SPF_request_free(SPF_request_t *sr)
60 {
61 SPF_ASSERT_NOTNULL(sr);
62 SPF_FREE(sr->client_dom);
63 SPF_FREE(sr->helo_dom);
64 SPF_FREE(sr->env_from);
65 SPF_FREE(sr->env_from_lp);
66 SPF_FREE(sr->env_from_dp);
67 free(sr);
68 }
69
70 SPF_errcode_t
SPF_request_set_ipv4(SPF_request_t * sr,struct in_addr addr)71 SPF_request_set_ipv4(SPF_request_t *sr, struct in_addr addr)
72 {
73 if (sr->client_dom) {
74 free(sr->client_dom);
75 sr->client_dom = NULL;
76 }
77 sr->client_ver = AF_INET;
78 sr->ipv4 = addr;
79 return SPF_E_SUCCESS;
80 }
81
82 SPF_errcode_t
SPF_request_set_ipv6(SPF_request_t * sr,struct in6_addr addr)83 SPF_request_set_ipv6(SPF_request_t *sr, struct in6_addr addr)
84 {
85 if (sr->client_dom) {
86 free(sr->client_dom);
87 sr->client_dom = NULL;
88 }
89 sr->client_ver = AF_INET6;
90 sr->ipv6 = addr;
91 return SPF_E_SUCCESS;
92 }
93
94 SPF_errcode_t
SPF_request_set_ipv4_str(SPF_request_t * sr,const char * astr)95 SPF_request_set_ipv4_str(SPF_request_t *sr, const char *astr)
96 {
97 struct in_addr addr;
98 if (astr == NULL)
99 astr = "0.0.0.0";
100 if (inet_pton(AF_INET, astr, &addr) <= 0)
101 return SPF_E_INVALID_IP4;
102 return SPF_request_set_ipv4(sr, addr);
103 }
104
105 SPF_errcode_t
SPF_request_set_ipv6_str(SPF_request_t * sr,const char * astr)106 SPF_request_set_ipv6_str(SPF_request_t *sr, const char *astr)
107 {
108 struct in6_addr addr;
109 if (astr == NULL)
110 astr = "::";
111 if (inet_pton(AF_INET6, astr, &addr) <= 0)
112 return SPF_E_INVALID_IP6;
113 return SPF_request_set_ipv6(sr, addr);
114 }
115
116 SPF_errcode_t
SPF_request_set_helo_dom(SPF_request_t * sr,const char * dom)117 SPF_request_set_helo_dom(SPF_request_t *sr, const char *dom)
118 {
119 SPF_ASSERT_NOTNULL(dom);
120 SPF_FREE(sr->helo_dom);
121 sr->helo_dom = strdup(dom);
122 if (! sr->helo_dom)
123 return SPF_E_NO_MEMORY;
124 /* set cur_dom and env_from? */
125 if (sr->env_from == NULL)
126 return SPF_request_set_env_from(sr, dom);
127 return SPF_E_SUCCESS;
128 }
129
130 const char *
SPF_request_get_rec_dom(SPF_request_t * sr)131 SPF_request_get_rec_dom(SPF_request_t *sr)
132 {
133 SPF_server_t *spf_server;
134 spf_server = sr->spf_server;
135 return spf_server->rec_dom;
136 }
137
138 int
SPF_request_set_env_from(SPF_request_t * sr,const char * from)139 SPF_request_set_env_from(SPF_request_t *sr, const char *from)
140 {
141 char *cp;
142 size_t len;
143
144 SPF_ASSERT_NOTNULL(from);
145 SPF_FREE(sr->env_from);
146 SPF_FREE(sr->env_from_lp);
147 SPF_FREE(sr->env_from_dp);
148
149 if (*from == '\0' && sr->helo_dom != NULL)
150 from = sr->helo_dom;
151 cp = strrchr(from, '@');
152 if (cp && (cp != from)) {
153 sr->env_from = strdup(from);
154 if (! sr->env_from)
155 return SPF_E_NO_MEMORY;
156
157 len = cp - from;
158 sr->env_from_lp = malloc(len + 1);
159 if (!sr->env_from_lp) {
160 SPF_FREE(sr->env_from);
161 return SPF_E_NO_MEMORY;
162 }
163 strncpy(sr->env_from_lp, from, len);
164 sr->env_from_lp[len] = '\0';
165 sr->env_from_dp = strdup(cp + 1);
166 if (!sr->env_from_dp) {
167 SPF_FREE(sr->env_from);
168 SPF_FREE(sr->env_from_lp);
169 return SPF_E_NO_MEMORY;
170 }
171 }
172 else {
173 if (cp == from) from++; /* "@domain.example" */
174 len = sizeof("postmaster@") + strlen(from);
175 sr->env_from = malloc(len + 1); /* sizeof("") == 1? */
176 if (! sr->env_from)
177 return SPF_E_NO_MEMORY;
178 sprintf(sr->env_from, "postmaster@%s", from);
179 sr->env_from_lp = strdup("postmaster");
180 if (!sr->env_from_lp) {
181 SPF_FREE(sr->env_from);
182 return SPF_E_NO_MEMORY;
183 }
184 sr->env_from_dp = strdup(from);
185 if (!sr->env_from_dp) {
186 SPF_FREE(sr->env_from);
187 SPF_FREE(sr->env_from_lp);
188 return SPF_E_NO_MEMORY;
189 }
190 }
191
192 return 0; // SPF_E_SUCCESS
193 }
194
195 const char *
SPF_request_get_client_dom(SPF_request_t * sr)196 SPF_request_get_client_dom(SPF_request_t *sr)
197 {
198 SPF_server_t *spf_server;
199
200 SPF_ASSERT_NOTNULL(sr);
201 spf_server = sr->spf_server;
202 SPF_ASSERT_NOTNULL(spf_server);
203
204 if (sr->client_dom == NULL) {
205 sr->client_dom = SPF_dns_get_client_dom(spf_server->resolver,
206 sr);
207 }
208 return sr->client_dom;
209 }
210
211 int
SPF_request_is_loopback(SPF_request_t * sr)212 SPF_request_is_loopback(SPF_request_t *sr)
213 {
214 if (sr->client_ver == AF_INET) {
215 if ((ntohl(sr->ipv4.s_addr) & IN_CLASSA_NET) ==
216 (IN_LOOPBACKNET << 24)) {
217 return TRUE;
218 }
219 }
220 else if (sr->client_ver == AF_INET6) {
221 if (IN6_IS_ADDR_LOOPBACK(&sr->ipv6))
222 return TRUE;
223 }
224 return FALSE;
225 }
226
227 static SPF_errcode_t
SPF_request_prepare(SPF_request_t * sr)228 SPF_request_prepare(SPF_request_t *sr)
229 {
230 if (sr->use_helo)
231 sr->cur_dom = sr->helo_dom;
232 else
233 sr->cur_dom = sr->env_from_dp;
234 return SPF_E_SUCCESS;
235 }
236
237 /**
238 * The common tail-end of a few methods below.
239 */
240 static SPF_errcode_t
SPF_request_query_record(SPF_request_t * spf_request,SPF_response_t * spf_response,SPF_record_t * spf_record,SPF_errcode_t err)241 SPF_request_query_record(SPF_request_t *spf_request,
242 SPF_response_t *spf_response,
243 SPF_record_t *spf_record,
244 SPF_errcode_t err)
245 {
246 if (err != SPF_E_SUCCESS) {
247 if (spf_record)
248 SPF_record_free(spf_record);
249 return err;
250 }
251 /* Now, in theory, SPF_response_errors(spf_response) == 0 */
252 if (SPF_response_errors(spf_response) > 0)
253 SPF_infof("Warning: %d errors in response, "
254 "but no error code. Evaluating.",
255 SPF_response_errors(spf_response));
256 /* If we get here, spf_record better not be NULL */
257 spf_response->spf_record_exp = spf_record;
258 err = SPF_record_interpret(spf_record,
259 spf_request, spf_response, 0);
260 SPF_record_free(spf_record);
261
262 return err;
263 }
264
265 /**
266 * The big entry point.
267 */
268 SPF_errcode_t
SPF_request_query_mailfrom(SPF_request_t * spf_request,SPF_response_t ** spf_responsep)269 SPF_request_query_mailfrom(SPF_request_t *spf_request,
270 SPF_response_t **spf_responsep)
271 {
272 SPF_server_t *spf_server;
273 SPF_record_t *spf_record;
274 SPF_errcode_t err;
275
276 SPF_ASSERT_NOTNULL(spf_request);
277 spf_server = spf_request->spf_server;
278 SPF_ASSERT_NOTNULL(spf_server);
279
280 *spf_responsep = SPF_response_new(spf_request);
281 if (! *spf_responsep)
282 return SPF_E_NO_MEMORY;
283
284 /* Give localhost a free ride */
285 if (SPF_request_is_loopback(spf_request))
286 return SPF_i_done(*spf_responsep, SPF_RESULT_PASS,
287 SPF_REASON_LOCALHOST, SPF_E_SUCCESS);
288
289 SPF_request_prepare(spf_request);
290
291 err = SPF_server_get_record(spf_server, spf_request,
292 *spf_responsep, &spf_record);
293 return SPF_request_query_record(spf_request, *spf_responsep,
294 spf_record, err);
295 }
296
297 /* This interface isn't finalised. */
298 SPF_errcode_t
SPF_request_query_fallback(SPF_request_t * spf_request,SPF_response_t ** spf_responsep,const char * record)299 SPF_request_query_fallback(SPF_request_t *spf_request,
300 SPF_response_t **spf_responsep,
301 const char *record)
302 {
303 SPF_server_t *spf_server;
304 SPF_record_t *spf_record;
305 SPF_errcode_t err;
306
307 SPF_ASSERT_NOTNULL(spf_request);
308 spf_server = spf_request->spf_server;
309 SPF_ASSERT_NOTNULL(spf_server);
310
311 *spf_responsep = SPF_response_new(spf_request);
312 if (! *spf_responsep)
313 return SPF_E_NO_MEMORY;
314
315 /* Give localhost a free ride */
316 if (SPF_request_is_loopback(spf_request))
317 return SPF_i_done(*spf_responsep, SPF_RESULT_PASS,
318 SPF_REASON_LOCALHOST, SPF_E_SUCCESS);
319
320 SPF_request_prepare(spf_request);
321
322 err = SPF_record_compile(spf_server,
323 *spf_responsep, &spf_record,
324 record);
325 return SPF_request_query_record(spf_request, *spf_responsep,
326 spf_record, err);
327 }
328
329 /**
330 * This replaces _2mx
331 *
332 * build record as SPF_VER_STR " mx:%s"
333 * Set cur_dom to the rcpt_to domain.
334 * Query on the 'fixed' 2mx record.
335 * Clobber the primary result.
336 */
337 /* FIXME: Check the implementation of this. */
338 SPF_errcode_t
SPF_request_query_rcptto(SPF_request_t * spf_request,SPF_response_t ** spf_responsep,const char * rcpt_to)339 SPF_request_query_rcptto(SPF_request_t *spf_request,
340 SPF_response_t **spf_responsep,
341 const char *rcpt_to)
342 {
343 SPF_server_t *spf_server;
344 SPF_record_t *spf_record;
345 SPF_errcode_t err;
346 const char *rcpt_to_dom;
347 char *record;
348 size_t len;
349
350 if ( (spf_request == NULL) || (rcpt_to == NULL) )
351 return SPF_E_INVALID_OPT;
352
353 SPF_ASSERT_NOTNULL(spf_request);
354 spf_server = spf_request->spf_server;
355 SPF_ASSERT_NOTNULL(spf_server);
356
357 *spf_responsep = SPF_response_new(spf_request);
358 if (! *spf_responsep)
359 return SPF_E_NO_MEMORY;
360
361 /* Give localhost a free ride */
362 if (SPF_request_is_loopback(spf_request))
363 return SPF_i_done(*spf_responsep, SPF_RESULT_PASS,
364 SPF_REASON_LOCALHOST, SPF_E_SUCCESS);
365
366 rcpt_to_dom = strchr(rcpt_to, '@');
367 if (rcpt_to_dom == NULL)
368 rcpt_to_dom = rcpt_to;
369 spf_request->cur_dom = rcpt_to_dom;
370
371 len = sizeof(SPF_VER_STR) + 64 + strlen(rcpt_to_dom);
372 record = malloc(len);
373 if (! record)
374 return SPF_E_NO_MEMORY;
375 snprintf(record, len, SPF_VER_STR " mx:%s", rcpt_to_dom);
376 err = SPF_record_compile(spf_server,
377 *spf_responsep, &spf_record,
378 record);
379 free(record);
380 return SPF_request_query_record(spf_request, *spf_responsep,
381 spf_record, err);
382 }
383