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