s/qmail 4.2.29a
Next generation secure email transport
Loading...
Searching...
No Matches
spfdnsip.c
Go to the documentation of this file.
1#include <unistd.h>
2#include "stralloc.h"
3#include "alloc.h"
4#include "ip.h"
5#include "ipalloc.h"
6#include "ipme.h"
7#include "str.h"
8#include "fmt.h"
9#include "scan.h"
10#include "byte.h"
11#include "now.h"
12#include "dns.h"
13#include "case.h"
14#include "spf.h"
15
16// shared by spf.c + spfdnsip.c
17
18extern stralloc dnsname;
19extern char ip4remote[4];
20extern char ip6remote[16];
21extern int flagip6;
22
30int match_ip4(unsigned char ip1[4],int prefix,char ip2[4])
31{
32 stralloc iptest1 = {0};
33 stralloc iptest2 = {0};
34
35 if (flagip6) return 0;
36
37 if (ip4_bytestring(&iptest1,ip1,prefix) == prefix)
38 if (ip4_bytestring(&iptest2,ip2,prefix) == prefix)
39 if (byte_diff(iptest1.s,prefix,iptest2.s)) return 0;
40
41 return 1;
42}
43
44int match_ip6(unsigned char ip1[16],int prefix,char ip2[16])
45{
46 stralloc iptest1 = {0};
47 stralloc iptest2 = {0};
48
49 if (!flagip6) return 0;
50
51 if (ip6_bytestring(&iptest1,ip1,prefix) == prefix)
52 if (ip6_bytestring(&iptest2,ip2,prefix) == prefix)
53 if (byte_diff(iptest1.s,prefix,iptest2.s)) return 0;
54
55 return 1;
56}
57
65int get_prefix(char *prefix)
66{
67 unsigned long r;
68 int pos;
69
70 if (!prefix || *prefix == '0') {
71 if (flagip6 == 0) return 32;
72 if (flagip6 == 1) return 128;
73 }
74
75 pos = scan_ulong(prefix,&r);
76 if (!pos || (prefix[pos] && !(prefix[pos] == '/'))) return SPF_SYNTAX;
77 if (flagip6 == 0 && r > 32) return SPF_SYNTAX;
78 if (flagip6 == 1 && r > 128) return SPF_SYNTAX;
79
80 return r;
81}
82
83/* DNS Record: -------------------------------------- Fetch multiple SPF TXT RRs */
84
93int spf_records(stralloc *spfrec,stralloc *domain)
94{
95 static stralloc out = {0};
96 static stralloc spf = {0};
97 int i, k;
98 int begin;
99 int r = 0;
100
101 begin = -1;
102
104 r = dns_txt(&out,(const stralloc *)domain);
105 switch (r) {
106 case DNS_MEM: return SPF_NOMEM;
107 case DNS_ERR: return SPF_DNSSOFT; /* return 2main */
108 case DNS_NXD: return SPF_NONE;
109 }
110 r = SPF_NONE;
111
112 for (k = 0; k < out.len; ++k) {
113 if (case_starts(out.s + k,"v=spf1")) {
114 begin = k;
115 break;
116 }
117 }
118
119 if (begin >= 0) {
120 if (case_starts(out.s + k + 6,"v=spf1")) return SPF_MULTIRR; /* return 2main */
121
122 if (!stralloc_copys(&spf,"")) return SPF_NOMEM;
123 for (i = begin; i < out.len; ++i) {
124 if (out.s[i] == '\r' || out.s[i] == '\n' || out.s[i] == '\0') break;
125 if (!stralloc_append(&spf,out.s + i)) return SPF_NOMEM;
126 }
127 if (!stralloc_0(&spf)) return SPF_NOMEM;
128 if (!stralloc_copys(spfrec,spf.s)) return SPF_NOMEM;
129
130 r = SPF_OK;
131 }
132
133 return r;
134}
135
136/* Mechanisms: -------------------------------------- Lookup functions */
137
145int spf_a(char *spfspec,char *prefix)
146{
147 stralloc sa = {0};
148 stralloc ip = {0};
149 int ipprefix, r, j;
150
151 ipprefix = get_prefix(prefix);
152 if (ipprefix < 0) return SPF_SYNTAX;
153
154 if (!stralloc_copys(&sa,spfspec)) return SPF_NOMEM;
155 if (!stralloc_readyplus(&ip,0)) return SPF_NOMEM;
156 if (!spf_info("MA/AAAA=",spfspec)) return SPF_NOMEM;
157
159
160 switch (dns_ip4(&ip,&sa)) {
161 case DNS_MEM: return SPF_NOMEM;
162 case DNS_ERR: r = SPF_DNSSOFT; break;
163 case DNS_NXD: r = SPF_NONE; break;
164 default:
165 r = SPF_NONE;
166 for (j = 0; j + 4 <= ip.len; j += 4)
167 if (match_ip4(ip.s + j,ipprefix,ip4remote))
168 return SPF_OK;
169 }
170
171 switch (dns_ip6(&ip,&sa)) {
172 case DNS_MEM: return SPF_NOMEM;
173 case DNS_ERR: r = SPF_DNSSOFT; break;
174 case DNS_NXD: r = SPF_NONE; break;
175 default:
176 r = SPF_NONE;
177 for (j = 0; j + 16 <= ip.len; j += 16)
178 if (match_ip6(ip.s + j,ipprefix,ip6remote))
179 return SPF_OK;
180 }
181
182 return r;
183}
184
192int spf_mx(char *spfspec,char *prefix)
193{
194 stralloc sa = {0};
195 ipalloc ia = {0};
196 unsigned long random;
197 int ipprefix;
198 int j, r;
199
200 ipprefix = get_prefix(prefix);
201 if (ipprefix < 0) return SPF_SYNTAX;
202
203 random = now() + (getpid() << 16);
204
205 if (!stralloc_copys(&sa,spfspec)) return SPF_NOMEM;
206 if (!spf_info("MMX=",spfspec)) return SPF_NOMEM;
207
208 switch (dns_mxip(&ia,&sa,random)) {
209 case DNS_MEM: return SPF_NOMEM;
210 case DNS_ERR: return SPF_DNSSOFT;
211 default:
212 r = SPF_NONE;
213 for (j = 0; j < ia.len; ++j) {
214 if (byte_diff(ip6remote,16,V6localnet) && !ip6_isv4mapped(ip6remote)) {
215 if (match_ip6(&ia.ix[j].addr.ip6.d,ipprefix,ip6remote))
216 return SPF_OK;
217 }
218 if (byte_diff(ip4remote,4,V4localnet)) {
219 if (match_ip4(&ia.ix[j].addr.ip4.d,ipprefix,ip4remote))
220 return SPF_OK;
221 }
222 }
223 }
224
225 return r;
226}
227
235int spf_ptr(char *spfspec,char *prefix)
236{
237 stralloc fqdn = {0};
238 stralloc out = {0};
239 stralloc ip = {0};
240 int slen = str_len(spfspec);
241 int rc, r;
242 int k = 0;
243 int pos;
244 int l = 0;
245
246 /* we didn't find host with the matching IP before */
247 if (dnsname.len == 7 && str_equal(dnsname.s,"unknown"))
248 return SPF_NONE;
249
250 if (!spf_info("MPTR=",spfspec)) return SPF_NOMEM;
251
252 /* the hostname found will probably be the same as before */
253 while (dnsname.len) {
254 pos = dnsname.len - slen;
255 if (pos < 0) break;
256 if (pos > 0 && dnsname.s[pos - 1] != '.') break;
257 if (case_diffb(dnsname.s + pos,slen,spfspec)) break;
258 return SPF_OK;
259 }
260
261 /* ok, either it's the first test or it's a very weired setup
262 Assumptions:
263 ip -> inverse DNS name (only one!)
264 inverse DNS name -> (same) ip (only one!)
265 */
266
267
268 if (!stralloc_readyplus(&fqdn,255)) return SPF_NOMEM;
269 if (!stralloc_readyplus(&out,255)) return SPF_NOMEM;
270 if (!stralloc_readyplus(&ip,32)) return SPF_NOMEM;
271
272 if (flagip6) {
273 rc = dns_name6(&out,ip6remote); // usually: 2. . .ip6.addr => only one
274 switch (rc) {
275 case DNS_MEM: return SPF_NOMEM;
276 case DNS_COM: r = SPF_DNSSOFT; break;
277 case DNS_ERR: r = SPF_NONE; break;
278 case DNS_NXD: r = SPF_NONE; break;
279 default: r = SPF_NONE; l++;
280 if (l > LOOKUP_LIMIT) { r = SPF_ERROR; break; }
281 switch (dns_ip6(&ip,&out)) { // theoretical more IPs cound be retrieved
282 case DNS_MEM: return SPF_NOMEM;
283 case DNS_ERR: r = SPF_DNSSOFT; break;
284 case DNS_NXD: r = SPF_NONE; break;
285 default: r = SPF_NONE;
286 for (k = 0; k + 16 <= ip.len; k += 16) {
287 if (k > 32 * LOOKUP_LIMIT) { r = SPF_ERROR; break; }
288 if (match_ip6(ip.s + k,128,ip6remote)) {
289 if (!dnsname.len)
290 if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM;
291 pos = out.len - slen;
292 if (pos < 0) continue;
293 if (pos > 0 && out.s[pos - 1] != '.') continue;
294 if (case_diffb(out.s + pos,slen,spfspec)) continue;
295
296 if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM;
297 r = SPF_OK;
298 }
299 }
300 }
301 }
302 } else { // IP4 branch
303 rc = dns_name4(&out,ip4remote); // usual answer: d.c.b.e.in-arpa.addr for IP4 a.b.c.d => only one
304 switch (rc) {
305 case DNS_MEM: return SPF_NOMEM;
306 case DNS_ERR: r = SPF_DNSSOFT; break;
307 case DNS_NXD: r = SPF_NONE; break;
308 default: r = SPF_NONE; l++;
309 if (l > LOOKUP_LIMIT) { r = SPF_ERROR; break; }
310 switch (dns_ip4(&ip,&out)) {
311 case DNS_MEM: return SPF_NOMEM;
312 case DNS_ERR: r = SPF_DNSSOFT; break;
313 case DNS_NXD: r = SPF_NONE; break;
314 default: r = SPF_NONE;
315 for (k = 0; k + 4 <= ip.len; k += 4) {
316 if (k > 32 * LOOKUP_LIMIT) { r = SPF_ERROR; break; }
317 if (match_ip4(ip.s + k,32,ip4remote)) {
318 if (!dnsname.len)
319 if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM;
320 pos = out.len - slen;
321 if (pos < 0) continue;
322 if (pos > 0 && out.s[pos - 1] != '.') continue;
323 if (case_diffb(out.s + pos,slen,spfspec)) continue;
324
325 if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM;
326 r = SPF_OK;
327 }
328 }
329 }
330 }
331 }
332 if (!dnsname.len)
333 if (!stralloc_copys(&dnsname,"unknown")) return SPF_NOMEM;
334
335 return r;
336}
337
345int spf_ip4(char *spfspec,char *prefix)
346{
347 char spfip[4];
348
349 if (flagip6) return SPF_NONE;
350 int ipprefix = get_prefix(prefix);
351
352 if (ipprefix < 0) return SPF_SYNTAX;
353 if (!ip4_scan(spfspec,spfip)) return SPF_SYNTAX;
354
355 if (!spf_info("MIPv4=",spfspec)) return SPF_NOMEM;
356 if (!match_ip4(spfip,ipprefix,ip4remote)) return SPF_NONE;
357
358 return SPF_OK;
359}
360
368int spf_ip6(char *spfspec,char *prefix)
369{
370 char spfip[16];
371
372 if (!flagip6) return SPF_NONE;
373 int ipprefix = get_prefix(prefix);
374
375 if (ipprefix < 0) return SPF_SYNTAX;
376 if (!ip6_scan(spfspec,spfip)) return SPF_SYNTAX;
377
378 if (!spf_info("MIPv6=",spfspec)) return SPF_NOMEM;
379 if (!match_ip6(spfip,ipprefix,ip6remote)) return SPF_NONE;
380
381 return SPF_OK;
382}
383
391int spf_exists(char *spfspec,char *prefix)
392{
393 stralloc sa = {0};
394 stralloc ip = {0};
395
396 if (!stralloc_copys(&sa,spfspec)) return SPF_NOMEM;
397 if (!spf_info("MExists=",spfspec)) return SPF_NOMEM;
398
399 switch (dns_ip4(&ip,&sa)) {
400 case DNS_MEM: return SPF_NOMEM;
401 case DNS_ERR: return SPF_DNSSOFT;
402 case DNS_NXD: return SPF_NONE;
403 default: return SPF_OK;
404 }
405
406}
int stralloc_copys(stralloc *, char const *)
int dns_mxip(ipalloc *ia, stralloc *sa, unsigned long random)
Definition: dns.c:105
#define DNS_NXD
Definition: dns.h:13
#define DNS_INIT
Definition: dns.h:12
stralloc out
Definition: dnscname.c:12
stralloc sa
Definition: dnscname.c:11
stralloc ia
Definition: dnsfq.c:17
datetime_sec now()
Definition: now.c:5
int j
Definition: qmail-send.c:920
uint32_t k[64]
Definition: sha256.c:26
stralloc domain
Definition: spf.c:34
#define SPF_ERROR
Definition: spf.h:16
#define SPF_OK
Definition: spf.h:22
#define SPF_NOMEM
Definition: spf.h:17
#define SPF_SYNTAX
Definition: spf.h:18
#define LOOKUP_LIMIT
Definition: spf.h:30
#define SPF_DNSSOFT
Definition: spf.h:28
#define SPF_NONE
Definition: spf.h:23
int spf_info(char *, const char *)
Definition: spf.c:640
#define SPF_MULTIRR
Definition: spf.h:14
int match_ip6(unsigned char ip1[16], int prefix, char ip2[16])
Definition: spfdnsip.c:44
int spf_exists(char *spfspec, char *prefix)
spf_exists (exists; exists:fqdn) simply looks for a A records only for SPF info and client host
Definition: spfdnsip.c:391
char ip4remote[4]
Definition: spf.c:38
int spf_mx(char *spfspec, char *prefix)
spf_mx (mx; mx:domain; mx:domain/24) compares MX records for SPF info and client host
Definition: spfdnsip.c:192
int get_prefix(char *prefix)
get_prefix return integer value of prefix length
Definition: spfdnsip.c:65
int spf_ptr(char *spfspec, char *prefix)
spf_ptr (ptr; ptr:fqdn) compares PTR records from SPF info and client host
Definition: spfdnsip.c:235
int spf_ip4(char *spfspec, char *prefix)
spf_ip4 (ip4; ip4:fqdn; ip4:fqdn/24) compares A records for SPF info and client host
Definition: spfdnsip.c:345
int match_ip4(unsigned char ip1[4], int prefix, char ip2[4])
match_ip compares IPv4/IPv6 addreses up to prefix length
Definition: spfdnsip.c:30
char ip6remote[16]
Definition: spf.c:39
int spf_records(stralloc *spfrec, stralloc *domain)
spf_records get TXT records for domain and extract SPF information
Definition: spfdnsip.c:93
int spf_ip6(char *spfspec, char *prefix)
spf_ip6 (ip6; ip6:fqdn; ip6:fqdn/56) compares AAAA records for SPF info and client host
Definition: spfdnsip.c:368
int spf_a(char *spfspec, char *prefix)
spf_a (a; a:fqdns; a:fqdns/56) compares A + AAAA records for SPF info and client host
Definition: spfdnsip.c:145
int flagip6
Definition: spf.c:32
stralloc dnsname
Definition: spf.c:27