1 /* $Id$ */
2
3 /*
4 *
5 * Copyright (C) 2004 David Mazieres (dm@uun.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 */
23
24 #include "asmtpd.h"
25
26 spfhosts_map smap;
27
28 #define SPF_VERS "v=spf1"
29
30 static int spftrace (getenv ("SPF_TRACE") ? atoi (getenv ("SPF_TRACE")) : 0);
31
32 static rxx spaceplus (" +");
33 static rxx domcidr ("^([\\w_\\-.]+)?(/(/)?(\\d{1,3}))?$");
34 static rxx ip4cidr ("^(\\d{1,3}(\\.\\d{1,3}){3})(/(\\d{1,3}))?$");
35
36 rxx &
linerx()37 spfhosts_map::linerx ()
38 {
39 static rxx spfrx ("^\\s*([^\\x00-\\x20\\x7f:]+)\\s*:\\s*(|\\S.*)$");
40 return spfrx;
41 }
42
43 bool
lookup(str * spfrecp,str domain)44 spfhosts_map::lookup (str *spfrecp, str domain)
45 {
46 if (!load ())
47 return false;
48
49 domain = mytolower (domain);
50 if (str *sp = table[domain]) {
51 *spfrecp = *sp;
52 return true;
53 }
54 const char *p = domain;
55 while ((p = strchr (p + 1, '.'))) {
56 if (str *sp = table[domain]) {
57 *spfrecp = *sp;
58 return true;
59 }
60 }
61 return false;
62 }
63
spf_t(in_addr a,str f,ptr<bhash<str>> lc,int rd)64 spf_t::spf_t (in_addr a, str f, ptr<bhash<str> > lc, int rd)
65 : addr (a), sender (f), fallback (NULL), tracedepth (0), recdepth (rd),
66 loopcheck (lc ? lc : ptr<bhash<str> > (New refcounted<bhash<str> >)),
67 ptr_cache_err (false), dnsrq (NULL), recurse (NULL)
68 {
69 }
70
~spf_t()71 spf_t::~spf_t ()
72 {
73 if (dnsrq)
74 dnsreq_cancel (dnsrq);
75 if (recurse)
76 delete recurse;
77 }
78
79 void
getexp(cbv cb,ptr<txtlist> t,int err)80 spf_t::getexp (cbv cb, ptr<txtlist> t, int err)
81 {
82 dnsrq = NULL;
83 if (t) {
84 strbuf sb;
85 for (u_int i = 0; i < t->t_ntxt; i++)
86 sb << t->t_txts[i];
87 explain = sb;
88 }
89 (*cb) ();
90 }
91
92 void
finish(spf_result stat)93 spf_t::finish (spf_result stat)
94 {
95 if (stat != SPF_PASS && !explain && expdn && expdn.len ()) {
96 if (!macro_subst (&expdn, expdn)) {
97 getptr (wrap (this, &spf_t::finish, stat));
98 return;
99 }
100 str n = expdn;
101 expdn = NULL;
102 if (dnsreq_t *d
103 = dns_txtbyname (n, wrap (this, &spf_t::getexp,
104 wrap (this, &spf_t::finish, stat))))
105 dnsrq = d;
106 return;
107 }
108 if (stat != SPF_PASS && explain && explain.len ()) {
109 if (!macro_subst (&explain, explain, true)) {
110 getptr (wrap (this, &spf_t::finish, stat));
111 return;
112 }
113 }
114
115 if (spftrace > !!tracedepth) {
116 warn ("SPF_TRACE: %*sRETURN ", tracedepth, "")
117 << inet_ntoa (addr) << " / "
118 << domain << " -> " << spf_print (stat) << "\n";
119 if (explain)
120 warn ("SPF_TRACE: %*sEXPLANATION ", tracedepth, "") << explain << "\n";
121 }
122 result = stat;
123 cb_t c (cb);
124 cb = NULL;
125 domain = NULL;
126 (*c) (this);
127 if (cb)
128 init ();
129 else
130 delete this;
131 }
132
133 bool
suffix_check(const char * targ,str suffix)134 spf_t::suffix_check (const char *targ, str suffix)
135 {
136 u_int n = strlen (targ);
137 if (n < suffix.len ())
138 return false;
139 if (n == suffix.len ())
140 return (!strcasecmp (targ, suffix));
141 const char *p = targ + n - suffix.len ();
142 if (p[-1] != '.')
143 return false;
144 return !strcasecmp (p, suffix);
145 }
146
147 void
getptr(cbv cb)148 spf_t::getptr (cbv cb)
149 {
150 if (dnsreq_t *d = dns_hostbyaddr (addr, wrap (this, &spf_t::getptr_2, cb)))
151 dnsrq = d;
152 }
153 void
getptr_2(cbv cb,ptr<hostent> h,int err)154 spf_t::getptr_2 (cbv cb, ptr<hostent> h, int err)
155 {
156 dnsrq = NULL;
157
158 if (spftrace >= 4)
159 printaddrs (sender, h, err);
160
161 if (dns_tmperr (err)) {
162 finish (SPF_ERROR);
163 return;
164 }
165 ptr_cache = h;
166 ptr_cache_err = err;
167 (*cb) ();
168 }
169
170 void
init()171 spf_t::init ()
172 {
173 redirect = NULL;
174 curmech = NULL;
175
176 if (!domain)
177 if (!(domain = extract_domain (sender)))
178 domain = sender;
179 if (!validate_domain (domain, true))
180 finish (SPF_UNKNOWN);
181 else if (spfrec)
182 parse_spf ();
183 else if (!loopcheck->insert (mytolower (domain)) || recdepth > 20) {
184 if (spftrace >= 2)
185 warn ("SPF_TRACE: %*sLOOP ", tracedepth, "") << domain << "\n";
186 finish (SPF_UNKNOWN);
187 }
188 else if (dnsreq_t *d = dns_txtbyname (domain, wrap (this, &spf_t::gettxt)))
189 dnsrq = d;
190 }
191
192 void
gettxt(ptr<txtlist> t,int err)193 spf_t::gettxt (ptr<txtlist> t, int err)
194 {
195 dnsrq = NULL;
196
197 if (spftrace >= 4)
198 printtxtlist (sender, t, err);
199
200 if (err) {
201 if (dns_tmperr (err))
202 finish (SPF_ERROR);
203 else if (fallback && fallback->lookup (&spfrec, domain))
204 parse_spf ();
205 else
206 finish (SPF_NONE);
207 return;
208 }
209
210 for (u_int i = 0; i < t->t_ntxt; i++)
211 if (!strncmp (t->t_txts[i], SPF_VERS, sizeof (SPF_VERS) - 1)
212 && (t->t_txts[i][sizeof (SPF_VERS) - 1] == ' '
213 || t->t_txts[i][sizeof (SPF_VERS) - 1] == '\0')) {
214 if (spfrec) {
215 finish (SPF_UNKNOWN);
216 return;
217 }
218 else
219 spfrec = t->t_txts[i] + sizeof (SPF_VERS) - 1;
220 }
221
222 if (spfrec)
223 parse_spf ();
224 else
225 finish (SPF_NONE);
226 }
227
228 void
parse_spf()229 spf_t::parse_spf ()
230 {
231 vec<str> dm;
232 split (&dm, spaceplus, spfrec);
233 if (spftrace > !!recdepth) {
234 warn ("SPF_TRACE: %*sCHECK ", tracedepth, "")
235 << inet_ntoa (addr) << " / " << domain << "\n";
236 if (spftrace >= 2) {
237 warn ("SPF_TRACE: %*sRULES", tracedepth, "");
238 for (str *rp = dm.base (); rp < dm.lim (); rp++)
239 warnx << " " << *rp;
240 warnx << "\n";
241 }
242 }
243
244 for (str *dmp = dm.base (); dmp < dm.lim (); dmp++) {
245 if (!dmp->len ())
246 continue;
247 const char *eq = strchr (*dmp, '=');
248 if (!strchr (*dmp, '='))
249 mechv.push_back (*dmp);
250 else {
251 const char *co = strchr (*dmp, ':');
252 if (co && co < eq)
253 mechv.push_back (*dmp);
254 else if (!strncasecmp (*dmp, "redirect=", 9))
255 redirect = dmp->cstr () + 9;
256 else if (!strncasecmp (*dmp, "exp=", 4)) {
257 expdn = dmp->cstr () + 4;
258 explain = NULL;
259 }
260 }
261 }
262
263 mech_start ();
264 }
265
266 /*
267 reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
268 "$" | ","
269
270 unreserved = alphanum | mark
271
272 mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
273
274 delims = "<" | ">" | "#" | "%" | <">
275
276 unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
277 */
278
279 inline bool
needsescape(u_char c)280 needsescape (u_char c)
281 {
282 if (isalnum (c))
283 return false;
284 if (c == '-' || c == '_' || c == '.' || c == '!' || c == '~'
285 || c == '*' || c == '\'' || c == '(' || c == ')')
286 return false;
287 return true;
288 }
289 static str
urlescape(str in)290 urlescape (str in)
291 {
292 strbuf sb;
293 for (const char *p = in; *p; p++)
294 if (needsescape (*p))
295 sb.fmt ("%%%02x", (u_char) *p);
296 else
297 sb.fmt ("%c", *p);
298 return sb;
299 }
300
301 bool
macro_subst_inner(const strbuf & out,str in,bool exp)302 spf_t::macro_subst_inner (const strbuf &out, str in, bool exp)
303 {
304 if (!in.len ())
305 return true;
306
307 str text;
308 bool escape = isupper (in[0]);
309 switch (tolower (in[0])) {
310 case 'l':
311 if (!(text = extract_local (sender)))
312 text = "postmaster";
313 break;
314 case 's':
315 if (extract_local (sender))
316 text = sender;
317 else
318 text = strbuf ("postmaster@%s", sender.cstr ());
319 break;
320 case 'o':
321 if (!(text = extract_domain (sender)))
322 text = sender;
323 break;
324 case 'd':
325 text = domain;
326 break;
327 case 'i':
328 text = inet_ntoa (addr);
329 break;
330 case 'p':
331 if (!ptrok ())
332 return false;
333 else if (ptr_cache)
334 text = ptr_cache->h_name;
335 else
336 text = "unknown";
337 break;
338 case 'v':
339 text = "in-addr";
340 break;
341 case 'h':
342 text = helo ? helo : str ("unknown");
343 break;
344 case 'r':
345 text = opt->hostname;
346 break;
347 case 'c':
348 if (exp) {
349 text = inet_ntoa (addr);
350 break;
351 }
352 /* cascade */
353 case 't':
354 if (exp) {
355 text = strbuf ("%lu", (u_long) time (NULL));
356 break;
357 }
358 /* cascade */
359 default:
360 out << "%{" << in << "}";
361 return true;
362 }
363
364 static rxx td ("^.(\\d+)?(r)?([\\.\\-\\+,\\/_=]+)?");
365 if (!td.match (in)) {
366 out << "%{" << in << "}";
367 return true;
368 }
369
370 str spn = td[3];
371 if (!spn)
372 spn = ".";
373 vec<str> parts;
374
375 const char *p = text;
376 while (*p) {
377 int n = strcspn (p, spn);
378 parts.push_back (str (p, n));
379 p += n;
380 if (*p)
381 p++;
382 }
383
384 if (td[2]) {
385 str *sp = parts.base (), *ep = parts.lim ();
386 while (sp + 1 < ep) {
387 str tmp = *--ep;
388 *ep = *sp;
389 *sp++ = tmp;
390 }
391 }
392
393 if (str ns = td[1]) {
394 u_int n = atoi (ns);
395 if (n < parts.size ())
396 parts.popn_front (parts.size () - n );
397 }
398
399 for (u_int i = 0; i < parts.size (); i++)
400 if (i)
401 out << "." << (escape ? urlescape (parts[i]) : parts[i]);
402 else
403 out << (escape ? urlescape (parts[i]) : parts[i]);
404
405 return true;
406 }
407
408 bool
macro_subst(str * outp,str in,bool exp)409 spf_t::macro_subst (str *outp, str in, bool exp)
410 {
411 strbuf out;
412 for (const char *p = in; *p; p++) {
413 if (*p != '%') {
414 out.fmt ("%c", *p);
415 continue;
416 }
417 switch (*++p) {
418 case '%':
419 out.fmt ("%%");
420 break;
421 case '_':
422 out.fmt (" ");
423 break;
424 case '-':
425 out.fmt ("%%20");
426 break;
427 case '{':
428 {
429 const char *r = strchr (p, '}');
430 if (!r) {
431 out << "%" << p;
432 if (spftrace)
433 warn << "malformed macro " << in << "\n";
434 *outp = out;
435 return true;
436 }
437 if (!macro_subst_inner (out, str (p + 1, r - p - 1), exp))
438 return false;
439 p = r;
440 break;
441 }
442 case '\0':
443 out.fmt ("%%");
444 if (spftrace >= 3)
445 warn ("SPF_TRACE: %*sMACRO ", tracedepth, "") << in << " -> ";
446 *outp = out;
447 if (spftrace >= 3)
448 warnx << *outp << "\n";
449 return true;
450 default:
451 out.fmt ("%%%c", *p);
452 break;
453 }
454 }
455 if (spftrace >= 3)
456 warn ("SPF_TRACE: %*sMACRO ", tracedepth, "") << in << " -> ";
457 *outp = out;
458 if (spftrace >= 3)
459 warnx << *outp << "\n";
460 return true;
461 }
462
463 bool
addr_check(int cidrlen,ref<hostent> h)464 spf_t::addr_check (int cidrlen, ref<hostent> h)
465 {
466 if (spftrace >= 5)
467 printaddrs ("addr_check", h);
468
469 u_int32_t mask = ip4_mask (cidrlen);
470 u_int32_t targ = addr.s_addr & mask;
471 for (char **ap = h->h_addr_list; *ap; ap++)
472 if (targ == (((in_addr *) *ap)->s_addr & mask))
473 return true;
474 return false;
475 }
476
477 void
mech_start()478 spf_t::mech_start ()
479 {
480 if (mechv.empty ()) {
481 if (redirect) {
482 curmech = (strbuf () << "redirect=" << redirect);
483 if (!macro_subst (&redirect, redirect)) {
484 getptr (wrap (this, &spf_t::mech_start));
485 return;
486 }
487 spfrec = NULL;
488 recdepth++;
489 if (spftrace >= 2)
490 warn ("SPF_TRACE: %*sREDIRECT ", tracedepth, "")
491 << domain << " -> " << redirect << "\n";
492 domain = redirect;
493 init ();
494 }
495 else {
496 curmech = NULL;
497 finish (SPF_NEUTRAL);
498 }
499 return;
500 }
501
502 curmech = mechv.pop_front ();
503 const char *mp = curmech;
504 switch (*mp) {
505 case '+':
506 case '-':
507 case '~':
508 case '?':
509 prefix = *mp++;
510 break;
511 default:
512 prefix = '+';
513 break;
514 }
515
516 const char *cp = strchr (mp, ':');
517 if (!cp)
518 cp = strchr (mp, '/');
519 str arg;
520 if (cp)
521 arg = *cp == ':' ? cp + 1 : cp;
522 str mech = mytolower (cp ? str (mp, cp - mp) : str (mp));
523
524 if (mech == "all")
525 mech_end (SPF_PASS);
526 else if (mech == "include")
527 mech_include (arg);
528 else if (mech == "a")
529 mech_a (arg);
530 else if (mech == "mx")
531 mech_mx (arg);
532 else if (mech == "ptr")
533 mech_ptr (arg);
534 else if (mech == "ip4")
535 mech_ip4 (arg);
536 else if (mech == "ip6")
537 mech_ip6 (arg);
538 else if (mech == "exists")
539 mech_exists (arg);
540 else
541 finish (SPF_UNKNOWN);
542 }
543
544 void
mech_end(spf_result res)545 spf_t::mech_end (spf_result res)
546 {
547 if (spftrace >= 2) {
548 warn ("SPF_TRACE: %*sTEST ", tracedepth, "");
549 if (curmech)
550 warnx << curmech << " -> ";
551 warnx << spf_print (res) << "\n";
552 }
553 switch (res) {
554 case SPF_PASS:
555 switch (prefix) {
556 case '+':
557 finish (SPF_PASS);
558 break;
559 case '-':
560 finish (SPF_FAIL);
561 break;
562 case '~':
563 finish (SPF_SOFTFAIL);
564 break;
565 case '?':
566 finish (SPF_NEUTRAL);
567 break;
568 }
569 break;
570 case SPF_FAIL:
571 case SPF_SOFTFAIL:
572 case SPF_NEUTRAL:
573 mech_start ();
574 break;
575 case SPF_NONE:
576 case SPF_ERROR:
577 case SPF_UNKNOWN:
578 finish (res);
579 break;
580 default:
581 panic ("unknown SPF state %d\n", res);
582 break;
583 }
584 }
585
586 void
mech_include(str targ)587 spf_t::mech_include (str targ)
588 {
589 if (!targ) {
590 mech_end (SPF_UNKNOWN);
591 return;
592 }
593 if (!macro_subst (&targ, targ)) {
594 getptr (wrap (this, &spf_t::mech_include, targ));
595 return;
596 }
597 if (spftrace >= 2)
598 warn ("SPF_TRACE: %*sINCLUDE ", tracedepth, "") << targ << " ...\n";
599 recurse = New spf_t (addr, sender, loopcheck, recdepth + 1);
600 recurse->cb = wrap (this, &spf_t::mech_include_2);
601 recurse->domain = targ;
602 recurse->helo = helo;
603 recurse->ptr_cache = ptr_cache;
604 recurse->tracedepth = tracedepth + 1;
605 recurse->init ();
606 }
607 void
mech_include_2(spf_t *)608 spf_t::mech_include_2 (spf_t *)
609 {
610 spf_result res = recurse->result;
611 bool error = (res == SPF_NONE || res == SPF_ERROR || res == SPF_UNKNOWN);
612 if (error || res == SPF_PASS || spftrace) {
613 strbuf sb;
614 sb << curmech << " (";
615 if (recurse->curmech)
616 sb << recurse->curmech << " -> ";
617 sb << spf_print (res) << ")";
618 curmech = sb;
619 }
620 recurse = NULL;
621 if (res == SPF_NONE)
622 res = SPF_UNKNOWN;
623 mech_end (res);
624 }
625
626 void
mech_a(str targ)627 spf_t::mech_a (str targ)
628 {
629 int cidrlen = 32;
630 if (!targ)
631 targ = domain;
632 else {
633 if (!macro_subst (&targ, targ)) {
634 getptr (wrap (this, &spf_t::mech_a, targ));
635 return;
636 }
637 if (!domcidr.match (targ) || domcidr[3]) {
638 mech_end (SPF_FAIL);
639 return;
640 }
641
642 if (str cl = domcidr[4])
643 cidrlen = atoi (domcidr[4]);
644 if (str d = domcidr[1])
645 targ = domcidr[1];
646 else
647 targ = domain;
648 }
649
650 if (dnsreq_t *d
651 = dns_hostbyname (targ, wrap (this, &spf_t::mech_a_2, cidrlen),
652 false, false))
653 dnsrq = d;
654 }
655 void
mech_a_2(int cidrlen,ptr<hostent> h,int err)656 spf_t::mech_a_2 (int cidrlen, ptr<hostent> h, int err)
657 {
658 dnsrq = NULL;
659
660 if (err)
661 mech_end (dns_tmperr (err) ? SPF_ERROR : SPF_FAIL);
662 else if (addr_check (cidrlen, h))
663 mech_end (SPF_PASS);
664 else
665 mech_end (SPF_FAIL);
666 }
667
668 void
mech_mx(str targ)669 spf_t::mech_mx (str targ)
670 {
671 int cidrlen = 32;
672 if (!targ)
673 targ = domain;
674 else {
675 if (!macro_subst (&targ, targ)) {
676 getptr (wrap (this, &spf_t::mech_a, targ));
677 return;
678 }
679 if (!domcidr.match (targ) || domcidr[3]) {
680 mech_end (SPF_FAIL);
681 return;
682 }
683
684 if (str cl = domcidr[4])
685 cidrlen = atoi (domcidr[4]);
686 if (str d = domcidr[1])
687 targ = domcidr[1];
688 else
689 targ = domain;
690 }
691
692 if (dnsreq_t *d
693 = dns_mxbyname (targ, wrap (this, &spf_t::mech_mx_2, cidrlen)))
694 dnsrq = d;
695 }
696 void
mech_mx_2(int cidrlen,ptr<mxlist> mxl,int err)697 spf_t::mech_mx_2 (int cidrlen, ptr<mxlist> mxl, int err)
698 {
699 dnsrq = NULL;
700
701 if (spftrace >= 5)
702 printmxlist ("mech_mx_2", mxl, err);
703
704 if (err)
705 mech_end (dns_tmperr (err) ? SPF_ERROR : SPF_FAIL);
706 else if (dnsreq_t *d
707 = dns_hostbyname (mxl->m_mxes[0].name,
708 wrap (this, &spf_t::mech_mx_3, cidrlen, mxl, 1),
709 false, false))
710 dnsrq = d;
711 }
712 void
mech_mx_3(int cidrlen,ptr<mxlist> mxl,int n,ptr<hostent> h,int err)713 spf_t::mech_mx_3 (int cidrlen, ptr<mxlist> mxl, int n, ptr<hostent> h, int err)
714 {
715 dnsrq = NULL;
716 if (h && addr_check (cidrlen, h))
717 mech_end (SPF_PASS);
718 else if (err && dns_tmperr (err))
719 mech_end (SPF_ERROR);
720 else if (n >= mxl->m_nmx)
721 mech_end (SPF_FAIL);
722 else if (dnsreq_t *d
723 = dns_hostbyname (mxl->m_mxes[n].name,
724 wrap (this, &spf_t::mech_mx_3, cidrlen, mxl, n+1),
725 false, false))
726 dnsrq = d;
727 }
728
729 void
mech_ptr(str targ)730 spf_t::mech_ptr (str targ)
731 {
732 if (!ptrok ()) {
733 getptr (wrap (this, &spf_t::mech_ptr, targ));
734 return;
735 }
736 if (!targ)
737 targ = domain;
738 macro_subst (&targ, targ);
739
740 if (!ptr_cache) {
741 mech_end (dns_tmperr (ptr_cache_err) ? SPF_ERROR : SPF_FAIL);
742 return;
743 }
744
745 if (suffix_check (ptr_cache->h_name, targ)) {
746 mech_end (SPF_PASS);
747 return;
748 }
749 for (char **np = ptr_cache->h_aliases; *np; np++)
750 if (suffix_check (targ, *np)) {
751 mech_end (SPF_PASS);
752 return;
753 }
754 mech_end (SPF_FAIL);
755 }
756
757 void
mech_ip4(str targ)758 spf_t::mech_ip4 (str targ)
759 {
760 in_addr a;
761 if (!targ || !ip4cidr.match (targ) || !inet_aton (ip4cidr[1], &a)) {
762 mech_end (SPF_UNKNOWN);
763 return;
764 }
765
766 u_int32_t mask;
767 if (str cl = ip4cidr[4])
768 mask = ip4_mask (atoi (cl));
769 else
770 mask = 0xffffffff;
771 if ((addr.s_addr & mask) == (a.s_addr & mask))
772 mech_end (SPF_PASS);
773 else
774 mech_end (SPF_FAIL);
775 }
776
777 void
mech_ip6(str targ)778 spf_t::mech_ip6 (str targ)
779 {
780 /* Never matches, no IPv6 support */
781 mech_end (SPF_FAIL);
782 }
783
784 void
mech_exists(str targ)785 spf_t::mech_exists (str targ)
786 {
787 if (!targ)
788 targ = domain;
789 else if (!macro_subst (&targ, targ)) {
790 getptr (wrap (this, &spf_t::mech_exists, targ));
791 return;
792 }
793
794 if (dnsreq_t *d = dns_hostbyname (targ,
795 wrap (this, &spf_t::mech_exists_2, targ),
796 false, false))
797 dnsrq = d;
798 }
799 void
mech_exists_2(str targ,ptr<hostent> h,int err)800 spf_t::mech_exists_2 (str targ, ptr<hostent> h, int err)
801 {
802 dnsrq = NULL;
803 if (spftrace >= 4)
804 printaddrs (targ, h, err);
805 if (h)
806 mech_end (SPF_PASS);
807 else if (dns_tmperr (err))
808 mech_end (SPF_ERROR);
809 else
810 mech_end (SPF_FAIL);
811 }
812
813 static void
spf_check_3(spfckcb_t cb,spf_result override,str omech,spf_t * spf)814 spf_check_3 (spfckcb_t cb, spf_result override, str omech, spf_t *spf)
815 {
816 spf_result res (spf->result);
817
818 if (override != SPF_INVALID) {
819 if (res == SPF_NEUTRAL || res == SPF_NONE || res == SPF_UNKNOWN) {
820 res = override;
821 (*cb) (res, spf->explain, omech);
822 }
823 else
824 (*cb) (res, spf->explain, spf->curmech);
825 return;
826 }
827
828 switch (res) {
829 case SPF_FAIL:
830 if (opt->spf_fail) {
831 spf->spfrec = opt->spf_fail;
832 spf->cb = wrap (spf_check_3, cb, res, spf->curmech);
833 spf->domain = NULL;
834 spf->explain = opt->spf_exp;
835 return;
836 }
837 break;
838 case SPF_NONE:
839 if (opt->spf_none) {
840 spf->spfrec = opt->spf_none;
841 spf->cb = wrap (spf_check_3, cb, res, spf->curmech);
842 spf->domain = NULL;
843 spf->explain = opt->spf_exp;
844 return;
845 }
846 break;
847 default:
848 break;
849 }
850 (*cb) (res, spf->explain, spf->curmech);
851 }
852 static void
spf_check_2(spfckcb_t cb,spf_t * spf)853 spf_check_2 (spfckcb_t cb, spf_t *spf)
854 {
855 spf_result res (spf->result);
856
857 switch (res) {
858 case SPF_PASS:
859 case SPF_FAIL:
860 case SPF_SOFTFAIL:
861 case SPF_ERROR:
862 (*cb) (res, spf->explain, NULL);
863 return;
864 default:
865 break;
866 }
867
868 spf->spfrec = NULL;
869 spf->explain = opt->spf_exp;
870 spf->cb = wrap (spf_check_3, cb, SPF_INVALID, str (NULL));
871 }
872 void
spf_check(in_addr a,str from,spfckcb_t cb,str helo,ptr<const hostent> ptrc)873 spf_check (in_addr a, str from, spfckcb_t cb,
874 str helo, ptr<const hostent> ptrc)
875 {
876 spf_t *spf = New spf_t (a, from);
877 spf->helo = helo;
878 spf->ptr_cache = ptrc;
879 spf->spfrec = opt->spf_local;
880 spf->explain = opt->spf_exp;
881 spf->fallback = &smap;
882 if (opt->spf_local)
883 spf->cb = wrap (spf_check_2, cb);
884 else
885 spf->cb = wrap (spf_check_3, cb, SPF_INVALID, str (NULL));
886 spf->init ();
887 }
888
889 const char *
spf_print(spf_result res)890 spf_print (spf_result res)
891 {
892 switch (res) {
893 case SPF_PASS:
894 return "pass";
895 case SPF_FAIL:
896 return "fail";
897 case SPF_SOFTFAIL:
898 return "softfail";
899 case SPF_NEUTRAL:
900 return "neutral";
901 case SPF_NONE:
902 return "none";
903 case SPF_ERROR:
904 return "error";
905 case SPF_UNKNOWN:
906 return "unknown";
907 default:
908 return "bad SPF result code";
909 }
910 }
911
912 const char *
spf1_print(spf_result res)913 spf1_print (spf_result res)
914 {
915 switch (res) {
916 case SPF_PASS:
917 return "Pass";
918 case SPF_FAIL:
919 return "Fail";
920 case SPF_SOFTFAIL:
921 return "SoftFail";
922 case SPF_NEUTRAL:
923 return "Neutral";
924 case SPF_NONE:
925 return "None";
926 case SPF_ERROR:
927 return "TempError";
928 case SPF_UNKNOWN:
929 return "PermError";
930 default:
931 return "bad SPF result code";
932 }
933 }
934