Back to index

courier  0.68.2
Classes | Functions
spf.c File Reference
#include "config.h"
#include "spf.h"
#include "rfc1035mxlist.h"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>

Go to the source code of this file.

Classes

struct  rfc1035_spf_info
struct  ptr_info

Functions

static void set_err_msg (char *errmsg_buf, size_t errmsg_buf_size, const char *errmsg)
static char lookup (struct rfc1035_spf_info *info)
char rfc1035_spf_lookup (const char *mailfrom, const char *tcpremoteip, const char *tcpremotehost, const char *helodomain, const char *mydomain, char *errmsg_buf, size_t errmsg_buf_size)
static int isspf1 (struct rfc1035_reply *reply, int n)
char rfc1035_spf_gettxt_res (const char *current_domain, char *buf, struct rfc1035_res *res)
char rfc1035_spf_gettxt (const char *current_domain, char *buf)
static unsigned get_words (char *record, char **wordptr)
static char spf_compute (char **words, struct rfc1035_spf_info *info)
char rfc1035_spf_compute (char *record, struct rfc1035_spf_info *info)
static char mechanism (const char *name, struct rfc1035_spf_info *info)
static void setexp (const char *name, struct rfc1035_spf_info *info)
static char * expand (const char *str, struct rfc1035_spf_info *info)
static int get_dual_cidr_length (const char *p)
static int ip_compare (const RFC1035_ADDR *a, const RFC1035_ADDR *b, int pfix)
static void get_domain_pfix (struct rfc1035_spf_info *info, const char *start, char **domain_ptr, int *pfix_ptr)
static void check_ptr (const char *ptr, void *void_arg)
static char do_ptr (const char *name, struct rfc1035_spf_info *info)
static char do_ipcheck (const char *name, struct rfc1035_spf_info *info, int pfix_add)
static int do_expand (const char *str, struct rfc1035_spf_info *info, void(*cb_func)(const char *, size_t n, void *), void *void_arg)
static void do_count (const char *p, size_t n, void *va)
static void do_save (const char *p, size_t n, void *va)
static char * get_macro (struct rfc1035_spf_info *info, char name)
static char * transform (char *macro, unsigned transformer_count, char transformer_reverse, char delimiter_char)
static int do_expand (const char *str, struct rfc1035_spf_info *info, void(*cb_func)(const char *, size_t, void *), void *void_arg)
static char * expandc (const char *ipaddr)
static char * expandi (const char *ipaddr)
static unsigned tsplit (char *macro, char delimiter, char **wordptr)

Class Documentation

struct rfc1035_spf_info

Definition at line 26 of file spf.c.

Class Members
const char * current_domain
char * errmsg_buf
size_t errmsg_buf_size
const char * helodomain
size_t * lookup_cnt
const char * mailfrom
const char * mydomain
const char * tcpremotehost
const char * tcpremoteip
struct ptr_info

Definition at line 536 of file spf.c.

Collaboration diagram for ptr_info:
Class Members
RFC1035_ADDR addr
int error
int found
struct rfc1035_spf_info * info
const char * name

Function Documentation

static void check_ptr ( const char *  ptr,
void *  void_arg 
) [static]

Definition at line 544 of file spf.c.

{
       struct ptr_info *pinfo=(struct ptr_info *)void_arg;
       RFC1035_ADDR *addr;
       unsigned addr_cnt;
       int rc;
       unsigned i;

       if (pinfo->found)
              return; /* No need */

       rc=rfc1035_a(&pinfo->info->res, ptr, &addr, &addr_cnt);

       if (rc > 0)
              pinfo->error=1;
       if (rc)
              return;

       for (i=0; i<addr_cnt; i++)
       {
              if (memcmp(&addr[i], &pinfo->addr,
                        sizeof(pinfo->addr)) == 0)
                     break;
       }

       if (i < addr_cnt)
       {
              size_t l1, l2;

              if (strcasecmp(ptr, pinfo->name) == 0)
                     pinfo->found=1;

              l1=strlen(ptr);
              l2=strlen(pinfo->name);

              if (l2 < l1)
              {
                     ptr=ptr+l1-l2;

                     if (ptr[-1] == '.' && strcasecmp(ptr, pinfo->name)==0)
                            pinfo->found=1;
              }
       }
       free(addr);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void do_count ( const char *  p,
size_t  n,
void *  va 
) [static]

Definition at line 1048 of file spf.c.

{
       *(size_t *)va += n;
}

Here is the caller graph for this function:

static int do_expand ( const char *  str,
struct rfc1035_spf_info info,
void(*)(const char *, size_t n, void *)  cb_func,
void *  void_arg 
) [static]

Here is the caller graph for this function:

static int do_expand ( const char *  str,
struct rfc1035_spf_info info,
void(*)(const char *, size_t, void *)  cb_func,
void *  void_arg 
) [static]

Definition at line 1098 of file spf.c.

{
       unsigned char alpha, lalpha;
       unsigned transformer_count;
       char transformer_reverse;
       char delimiter_char;
       char *macro;

       /*
       **     macro-string = *( macro-char / VCHAR )
       **     macro-char   = ( "%{" ALPHA transformer *delimiter "}" )
       **                    / "%%" / "%_" / "%-"
       **     transformer  = [ *DIGIT ] [ "r" ]
       **     delimiter    = "." / "-" / "+" / "," / "/" / "_" / "="
       **
       */

       while (*str)
       {
              size_t i;

              for (i=0; str[i]; i++)
                     if (str[i] == '%')
                            break;

              if (i)
              {
                     (*cb_func)(str, i, void_arg);
                     str += i;
                     continue;
              }

              /*
              **   A literal "%" is expressed by "%%".
              **   %_ expands to a single " " space.
              **   %- expands to a URL-encoded space, viz. "%20".
              */

              switch (str[i+1]) {
              case '{':
                     break;
              case '%':
                     (*cb_func)("%", 1, void_arg);
                     str += 2;
                     continue;
              case '_':
                     (*cb_func)(" ", 1, void_arg);
                     str += 2;
                     continue;
              case '-':
                     (*cb_func)("%20", 3, void_arg);
                     str += 2;
                     continue;
              default:
                     ++str;
                     continue;
              }

              str += 2;

              if (!*str)
                     continue;
              alpha=(unsigned char)*str++;
              transformer_count=0;
              while (*str && isdigit((unsigned char)*str))
              {
                     transformer_count=transformer_count * 10 +
                            (*str++ - '0');
              }

              transformer_reverse=0;
              delimiter_char=0;

              while (*str && *str != '}')
              {
                     switch (*str) {
                     case 'r':
                     case 'R':
                            transformer_reverse='r';
                            break;
                     case '.':
                     case '-':
                     case '+':
                     case ',':
                     case '/':
                     case '_':
                     case '=':
                            delimiter_char= *str;
                            break;
                     }
                     ++str;
              }
              lalpha=tolower(alpha);

              macro=get_macro(info, lalpha);
              if (macro && (transformer_reverse || transformer_count))
              {
                     char *new_macro=transform(macro, transformer_count,
                                            transformer_reverse,
                                            delimiter_char);

                     free(macro);
                     macro=new_macro;
              }

              if (macro && lalpha != alpha)
              {
                     static const char validchars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
                     size_t l=1;
                     size_t i,j;
                     char *p;

                     for (i=0; macro[i]; i++)
                     {
                            ++l;
                            if (strchr(validchars, macro[i]) == NULL)
                                   l += 2;
                     }

                     p=malloc(l);
                     for (i=j=0; p && macro[i]; i++)
                     {
                            if (strchr(validchars, macro[i]))
                            {
                                   p[j]=macro[i];
                                   ++j;
                            }
                            else
                            {
                                   sprintf(p+j, "%%%02X",
                                          (int)(unsigned char)macro[i]);
                                   j += 3;
                            }
                     }
                     if (p)
                            p[j]=0;
                     free(macro);
                     macro=p;
              }

              if (macro == NULL)
              {
                     set_err_msg(info->errmsg_buf,
                                info->errmsg_buf_size,
                                strerror(errno));
                     return -1;
              }

              (*cb_func)(macro, strlen(macro), void_arg);
              free(macro);
              if (*str == '}')
                     ++str;
       }
       return 0;
}

Here is the call graph for this function:

static char do_ipcheck ( const char *  name,
struct rfc1035_spf_info info,
int  pfix_add 
) [static]

Definition at line 647 of file spf.c.

{
       char *addrptr;
       char *p;
       int pfix;
       RFC1035_ADDR addr, addrcmp;

       /*
       ** These mechanisms test if the <sending-host> falls into a
       ** given IP network.
       **
       ** The <sending-host> is compared to the given network.  If
       ** they match, the mechanism matches.
       */

       if (rfc1035_aton(info->tcpremoteip, &addr) < 0)
       {
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         "Invalid tcpremoteip.\n");
              return SPF_FAIL;
       }

       if ((addrptr=strdup(name+4)) == NULL)
       {
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         strerror(errno));
              return SPF_ERROR;
       }

       p=strrchr(addrptr, '/');
       pfix=sizeof(RFC1035_ADDR)*8;

       if (p)
       {
              *p++=0;
              pfix=atol(p)+pfix_add;
       }

       if (rfc1035_aton(addrptr, &addrcmp) < 0)
       {
              free(addrptr);
              return SPF_FAIL;
       }
       free(addrptr);
       if (ip_compare(&addr, &addrcmp, pfix))
              return SPF_PASS;
       return SPF_FAIL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char do_ptr ( const char *  name,
struct rfc1035_spf_info info 
) [static]

Definition at line 590 of file spf.c.

{
       struct ptr_info pinfo;

       if (*name++ == ':')
              pinfo.name=name;
       else
              pinfo.name=strrchr(info->mailfrom, '@')+1;

       pinfo.info=info;
       pinfo.found=0;
       pinfo.error=0;

       /*
       ** First the <sending-host>'s name is looked up using this
       ** procedure:
       **
       ** perform a PTR lookup against the <sending-host>'s IP.  For
       ** each record returned, validate the host name by looking up
       ** its IP address.  If the <sending-host>'s IP is among the
       ** returned IP addresses, then that host name is validated.
       **
       ** Check all validated hostnames to see if they end in the
       ** <target-name> domain.  If any do, this mechanism matches.
       ** If no validated hostname can be found, or if none of the
       ** validated hostnames end in the <target-name>, this
       ** mechanism fails to match.
       */

       if (rfc1035_aton(info->tcpremoteip, &pinfo.addr) < 0)
       {
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         "Invalid tcpremoteip.\n");
              return SPF_FAIL;
       }

       if (rfc1035_ptr_x(&info->res, &pinfo.addr,
                       check_ptr, &pinfo) < 0)
       {
              if (errno == ENOENT)
                     return SPF_FAIL;

              return SPF_ERROR;
       }

       if (pinfo.found)
              return SPF_PASS;
       if (pinfo.error)
       {
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         "ptr lookup failed.\n");
              return SPF_UNKNOWN;
       }
       return SPF_FAIL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void do_save ( const char *  p,
size_t  n,
void *  va 
) [static]

Definition at line 1053 of file spf.c.

{
       char **b=(char **)va;

       memcpy(*b, p, n);
       *b += n;
}

Here is the caller graph for this function:

static char * expand ( const char *  str,
struct rfc1035_spf_info info 
) [static]

Definition at line 1061 of file spf.c.

{
       size_t cnt=1;
       char *buf;
       char *p;

       if (do_expand(str, info, do_count, &cnt) < 0)
              return NULL;

       buf=malloc(cnt);

       if (!buf)
       {
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         strerror(errno));
              return NULL;
       }

       p=buf;
       if (do_expand(str, info, do_save, &p) < 0)
       {
              free(buf);
              return NULL;
       }

       *p=0;
       return buf;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char * expandc ( const char *  ipaddr) [static]

Definition at line 1320 of file spf.c.

{
       if (strncmp(ipaddr, "::ffff:", 7) == 0)
              return strdup(ipaddr+7);
       return strdup(ipaddr);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char * expandi ( const char *  ipaddr) [static]

Definition at line 1327 of file spf.c.

{
       if (strchr(ipaddr, ':') &&
           strncmp(ipaddr, "::ffff:", 7))
       {
              RFC1035_ADDR addr;

              if (rfc1035_aton(ipaddr, &addr) == 0)
              {
                     char name[sizeof(addr)*4+1];
                     char *p=name;
                     int i;
                     unsigned char *q=(unsigned char *)&addr;

                     for (i=0; i<sizeof(addr); i++)
                     {
                            sprintf(p, "%s%x.%x", i ? ".":"",
                                   (int)((q[i] >> 4) & 0x0F),
                                   (int)(q[i] & 0x0F));
                            p += strlen(p);
                     }
                     return strdup(name);
              }

       }
       return expandc(ipaddr);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void get_domain_pfix ( struct rfc1035_spf_info info,
const char *  start,
char **  domain_ptr,
int *  pfix_ptr 
) [static]

Definition at line 496 of file spf.c.

{
       *pfix_ptr=sizeof(RFC1035_ADDR)*8;

       if (*start == 0 || *start == '/')
       {
              *domain_ptr=strdup(strrchr(info->mailfrom, '@')+1);

              if (*start == '/')
                     *pfix_ptr=get_dual_cidr_length(start);
       }
       else
       {
              char *p;

              *domain_ptr=strdup(*start == ':' ? start+1:start);

              p=strchr(*domain_ptr, '/');
              if (p)
              {
                     *pfix_ptr=get_dual_cidr_length(p);
                     *p++=0;
              }

              if (*domain_ptr == 0)
              {
                     free(*domain_ptr);
                     *domain_ptr=strdup(strrchr(info->mailfrom,
                                             '@')+1);
              }
       }

       if (!*domain_ptr)
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         strerror(errno));
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int get_dual_cidr_length ( const char *  p) [static]

Definition at line 453 of file spf.c.

{
#if RFC1035_IPV6
       const char *q;

       for (q=p; *q; q++)
              if (*q == '/' && q[1] == '/')
                     return atoi(q+2);

       return atoi(p+1)+12*8;
#else
       if (p[1] == '/')
              return -1;
       return atoi(p+1);
#endif
}

Here is the caller graph for this function:

static char * get_macro ( struct rfc1035_spf_info info,
char  name 
) [static]

Definition at line 1259 of file spf.c.

{
       char *p;
       const char *cp;

       switch (name) {
       case 'l':
              /* l = local-part of responsible-sender */

              cp=strrchr(info->mailfrom, '@');
              p=malloc(cp-info->mailfrom+1);
              if (!p)
                     return p;
              memcpy(p, info->mailfrom, cp-info->mailfrom);
              p[cp-info->mailfrom]=0;
              return p;
       case 's':
              /* s = responsible-sender */
              return strdup(info->mailfrom);
       case 'o':
              return strdup(strrchr(info->mailfrom, '@')+1);
              /* o = responsible-domain */
       case 'd':
              return strdup(info->current_domain);
              /* d = current-domain */
       case 'c':
              return expandc(info->tcpremoteip);
              /* c = SMTP client IP (easily readable format) */
       case 'i':
              return expandi(info->tcpremoteip);
              /* i = SMTP client IP (nibble format when an IPv6 address) */
       case 'p':
              return strdup(info->tcpremotehost);
              /* p = SMTP client domain name */
       case 'v':
              return (strdup(strchr(info->tcpremoteip, ':') &&
                            strncmp(info->tcpremoteip, "::ffff:", 7)
                            ? "ip6":"in-addr"));
              /* v = client IP version string: "in-addr" for ipv4 or "ip6" for ipv6 */
       case 'h':
       /* h = HELO/EHLO domain */
              return strdup(info->helodomain);
       case 'r':
       /* r = receiving domain */
              return strdup(info->mydomain);
       }

       return strdup("");
}

Here is the call graph for this function:

Here is the caller graph for this function:

static unsigned get_words ( char *  record,
char **  wordptr 
) [static]

Definition at line 266 of file spf.c.

{
       unsigned n=0;

       while (*record)
       {
              if (isspace((int)(unsigned char)*record))
              {
                     ++record;
                     continue;
              }

              if (wordptr)
                     *wordptr++=record;
              ++n;

              while (*record)
              {
                     if (isspace((int)(unsigned char)*record))
                            break;
                     ++record;
              }

              if (*record && wordptr)
                     *record++=0;
       }
       return n;
}

Here is the caller graph for this function:

static int ip_compare ( const RFC1035_ADDR *  a,
const RFC1035_ADDR *  b,
int  pfix 
) [static]

Definition at line 470 of file spf.c.

{
       const unsigned char *ca, *cb;
       unsigned i;

       if (pfix < 0)
              return 0;

       ca=(const unsigned char *)a;
       cb=(const unsigned char *)b;

       for (i=0; i<sizeof(RFC1035_ADDR); i++)
       {
              int bits=pfix>8?8:pfix;
              unsigned char m=(unsigned char )(~0 << (8-bits));

              if ((ca[i] & m) != (cb[i] & m))
                     return 0;

              pfix -= bits;
       }
       return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int isspf1 ( struct rfc1035_reply reply,
int  n 
) [static]

Definition at line 150 of file spf.c.

{
       char   txtbuf[256];
       const char *p;

       rfc1035_rr_gettxt(reply->allrrs[n], 0, txtbuf);

       for (p=txtbuf; *p; p++)
              if (!isspace((int)(unsigned char)*p))
                     break;

       if (strncasecmp(p, "v=spf1", 6) == 0 &&
           (p[6] == 0 ||
            isspace((int)(unsigned char)p[6])))
              return 1;

       return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char lookup ( struct rfc1035_spf_info info) [static]

Definition at line 1011 of file spf.c.

{
       char record[256];
       char c;

       /*
       ** 
       ** If a loop is detected, or if more than 20 subqueries are triggered,
       ** an SPF client MAY abort the lookup and return the result "unknown".
       */

       if (++*info->lookup_cnt > 20)
       {
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         "Maximum of 20 nested SPF queries exceeded.");
              return SPF_UNKNOWN;
       }

       c=rfc1035_spf_gettxt(info->current_domain, record);

       if (c != SPF_PASS)
              return c;

       return rfc1035_spf_compute(record, info);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char mechanism ( const char *  name,
struct rfc1035_spf_info info 
) [static]

Definition at line 697 of file spf.c.

{
       if (strcasecmp(name, "all") == 0)
       {
              /*
              ** The "all" mechanism is a test that always matches.  It is
              ** used as the rightmost mechanism in an SPF record to
              ** provide an explicit default.
              */
              return SPF_PASS;
       }

       if (strncasecmp(name, "include:", 8) == 0)
       {
              char *new_domain;
              struct rfc1035_spf_info newinfo;
              char rc;

              /*
              ** The "include" mechanism triggers a recursive SPF query.  The
              ** domain-spec is expanded as per section 7.  Then a new query
              ** is launched using the resulting string as the
              ** <current-domain>.  The <responsible-sender> stays the same.
              */

              new_domain=expand(name+8, info);

              if (!new_domain)
                     return SPF_ERROR;

              newinfo= *info;
              newinfo.current_domain=new_domain;

              /*
              **      included    include
              **      query       mechanism      SPF
              **      result      result         processing
              **      -------- -- -------------- -------------------------------------
              **      pass     => match,         return the prefix value for "include"
              **      fail     => no match,      continue processing
              **      softfail => no match,      continue processing
              **      neutral  => no match,      continue processing
              **      error    => throw error,   abort processing, return error
              **      unknown  => throw unknown, abort processing, return unknown
              **      none     => throw unknown, abort processing, return unknown
              */

              rc=lookup(&newinfo);
              free(new_domain);

              switch (rc) {
              case SPF_PASS:
                     return SPF_PASS;
              case SPF_FAIL:
              case SPF_SOFTFAIL:
              case SPF_NEUTRAL:
                     return SPF_FAIL;
              case SPF_ERROR:
                     return SPF_ERROR;
              default:
                     return SPF_UNKNOWN;
              }
       }

       if (strncasecmp(name, "a", 1) == 0 &&
           (name[1] == 0 || name[1] == ':' || name[1] == '/'))
       {
              char *domain_spec;
              int pfix;
              RFC1035_ADDR addr;

              RFC1035_ADDR *iaptr;
              unsigned iasize;
              int rc;
              unsigned ii;

              /*
              ** This mechanism matches if the <sending-host> is one of the
              ** <target-name>'s IP addresses.
              **
              ** A = "a" [ ":" domain-spec ] [ dual-cidr-length ]
              **
              ** The <sending-host> is compared to the IP address(es) of the
              ** <target-name>.  If any address matches, the mechanism
              ** matches.
              */

              get_domain_pfix(info, name+1, &domain_spec, &pfix);

              if (!domain_spec)
                     return SPF_ERROR;

              if (rfc1035_aton(info->tcpremoteip, &addr) < 0)
              {
                     free(domain_spec);
                     set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                                "Invalid tcpremoteip.\n");
                     return SPF_FAIL;
              }

              rc=rfc1035_a(&info->res,
                          domain_spec,
                          &iaptr,
                          &iasize);

              free(domain_spec);

              if (rc != 0)
              {
                     set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                                "IP address lookup failed.\n");
                     return SPF_UNKNOWN;
              }

              for (ii=0; ii<iasize; ii++)
                     if (ip_compare(&addr, iaptr+ii, pfix))
                     {
                            free(iaptr);
                            return SPF_PASS;
                     }

              free(iaptr);
              return SPF_FAIL;
       }


       if (strncasecmp(name, "mx", 2) == 0 &&
           (name[2] == 0 || name[2] == ':' || name[2] == '/'))
       {
              char *domain_spec;
              int pfix;
              int rc;
              struct rfc1035_mxlist *mxlist, *mxp;
              RFC1035_ADDR addr;

              /*
              ** This mechanism matches if the <sending-host> is one of the
              ** MX hosts for a domain name.
   
              ** MX = "mx" [ ":" domain-spec ] [ dual-cidr-length ]
    
              ** SPF clients first perform an MX lookup on the <target-name>.
              ** SPF clients then perform an A lookup on each MX name
              ** returned, in order of MX priority.  The <sending-host> is
              ** compared to each returned IP address.  If any address
              ** matches, the mechanism matches.
              */

              get_domain_pfix(info, name+2, &domain_spec, &pfix);

              if (!domain_spec)
                     return SPF_ERROR;

              if (rfc1035_aton(info->tcpremoteip, &addr) < 0)
              {
                     free(domain_spec);
                     set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                                "Invalid tcpremoteip.\n");
                     return SPF_FAIL;
              }

              rc=rfc1035_mxlist_create_x(&info->res,
                                      domain_spec, 0,
                                      &mxlist);
              free(domain_spec);
              if (rc)
              {
                     rfc1035_mxlist_free(mxlist);
                     set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                                "DNS MX lookup failed.\n");
                     return SPF_ERROR;
              }

              for (mxp=mxlist; mxp; mxp=mxp->next)
              {
                     RFC1035_ADDR addrcmp;

                     if (rfc1035_sockaddrip(&mxp->address,
                                          sizeof(mxp->address),
                                          &addrcmp) < 0)
                            continue;

                     if (ip_compare(&addr, &addrcmp, pfix))
                     {
                            rfc1035_mxlist_free(mxlist);
                            return SPF_PASS;
                     }
              }
              rfc1035_mxlist_free(mxlist);
              return SPF_FAIL;
       }

       if (strncasecmp(name, "ip4:", 4) == 0)
       {
              if (strchr(name+4, ':'))
                     return SPF_FAIL; /* What does IPv6 addr doing here? */

#if RFC1035_IPV6
              return do_ipcheck(name, info, 12*8);
#else
              return do_ipcheck(name, info, 0);
#endif
       }

       if (strncasecmp(name, "ip6:", 4) == 0)
       {
#if RFC1035_IPV6
              return do_ipcheck(name, info, 0);
#else
              return SPF_FAIL;
#endif
       }

       if (strncasecmp(name, "ptr", 3) == 0 &&
           (name[3] == 0 || name[3] == ':'))
       {
              return do_ptr(name+3, info);
       }

       if (strncasecmp(name, "exists:", 7) == 0)
       {
              char *domain_spec;
              RFC1035_ADDR *iaptr;
              unsigned iasize;
              int rc;

              /*
              ** This mechanism is used to construct an arbitrary host name
              ** that is used for a DNS A record query.  It allows for
              ** complicated schemes involving arbitrary parts of the mail
              ** envelope to determine what is legal.
              **
              ** exists = "exists" ":" domain-spec
              **
              ** The domain-spec is expanded as per Section 7.  The
              ** resulting domain name is used for a DNS A lookup.  If any
              ** A record is returned, this mechanism matches.  The lookup
              ** type is 'A' even when the connection type is IPv6.
              */

              domain_spec=expand(name+7, info);
              if (!domain_spec)
                     return SPF_ERROR;

              rc=rfc1035_a(&info->res,
                          domain_spec,
                          &iaptr,
                          &iasize);
              free(domain_spec);

              if (rc < 0)
                     return SPF_FAIL;
              if (rc > 0)
                     return SPF_ERROR;
              free(iaptr);
              return SPF_PASS;
       }

       return SPF_FAIL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

char rfc1035_spf_compute ( char *  record,
struct rfc1035_spf_info info 
)

Definition at line 298 of file spf.c.

{
       unsigned n=get_words(record, NULL);
       char **words=malloc((n+1)*sizeof(char *));
       char rc;

       if (words == NULL)
       {
              set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
                         strerror(errno));
              return SPF_ERROR;
       }

       get_words(record, words);
       words[n]=0;

       rc=spf_compute(words, info);
       free(words);
       return rc;
}

Here is the call graph for this function:

Here is the caller graph for this function:

char rfc1035_spf_gettxt ( const char *  current_domain,
char *  buf 
)

Definition at line 245 of file spf.c.

{
       struct rfc1035_res res;
       char c;

       rfc1035_init_resolv(&res);

       c=rfc1035_spf_gettxt_res(current_domain, buf, &res);

       rfc1035_destroy_resolv(&res);

       return c;
}

Here is the call graph for this function:

Here is the caller graph for this function:

char rfc1035_spf_gettxt_res ( const char *  current_domain,
char *  buf,
struct rfc1035_res res 
)

Definition at line 169 of file spf.c.

{
       struct rfc1035_reply *reply;
       char   namebuf[RFC1035_MAXNAMESIZE+1];
       int n, o;


       namebuf[0]=0;
       strncat(namebuf, current_domain, RFC1035_MAXNAMESIZE);

       if (rfc1035_resolve_cname(res, namebuf,
                              RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
                              &reply, 0) < 0 ||
           reply == 0 ||
           (n=rfc1035_replysearch_an(res, reply, namebuf, RFC1035_TYPE_TXT,
                                  RFC1035_CLASS_IN, 0)) < 0)
       {
              switch (reply ? reply->rcode:
                     RFC1035_RCODE_SERVFAIL) {
              case RFC1035_RCODE_NOERROR:
                     rfc1035_replyfree(reply);
                     return SPF_NONE;
              case RFC1035_RCODE_NXDOMAIN:
                     rfc1035_replyfree(reply);
                     return SPF_UNKNOWN;
              default:
                     break;
              }
              if (reply)
                     rfc1035_replyfree(reply);
              return SPF_ERROR;
       }

       while (n >= 0)
       {
              if (isspf1(reply, n))
                     break;

              n=rfc1035_replysearch_an(res, reply, namebuf,
                                    RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
                                    n+1);
       }

       if (n >= 0)
       {
              for (o=n; (o=rfc1035_replysearch_an(res, reply, namebuf,
                                              RFC1035_TYPE_TXT,
                                              RFC1035_CLASS_IN,
                                              o+1)) >= 0; )
              {

                     /*
                     **
                     ** A domain MUST NOT return multiple records that
                     ** begin with the version "v=spf1".  If more than
                     ** one "v=spf1" record is returned, this constitutes
                     ** a syntax error and the result is "unknown".
                     */

                     if (isspf1(reply, o))
                     {
                            rfc1035_replyfree(reply);
                            return SPF_UNKNOWN;
                     }
              }

              rfc1035_rr_gettxt(reply->allrrs[n], 0, buf);
              rfc1035_replyfree(reply);
              return SPF_PASS;
       }
       rfc1035_replyfree(reply);
       return SPF_UNKNOWN;
}

Here is the call graph for this function:

Here is the caller graph for this function:

char rfc1035_spf_lookup ( const char *  mailfrom,
const char *  tcpremoteip,
const char *  tcpremotehost,
const char *  helodomain,
const char *  mydomain,
char *  errmsg_buf,
size_t  errmsg_buf_size 
)

Definition at line 60 of file spf.c.

{
       size_t lookup_cnt=0;
       struct rfc1035_spf_info info;
       char result;

       if (!tcpremoteip) tcpremoteip="";
       if (!tcpremotehost) tcpremotehost="";
       if (!helodomain) helodomain="";
       if (!mydomain) mydomain="";

       if (errmsg_buf && errmsg_buf_size)
              *errmsg_buf=0;

       /*
       ** If the <responsible-sender> has no localpart, clients MUST
       ** substitute the string "postmaster" for the localpart.
       */
       if (strchr(mailfrom, '@') == NULL)
       {
              char *buf=malloc(sizeof("postmaster@")+strlen(mailfrom));
              char err_code;

              if (buf == NULL)
              {
                     set_err_msg(errmsg_buf, errmsg_buf_size,
                                strerror(errno));
                     return SPF_ERROR;
              }

              err_code=rfc1035_spf_lookup(strcat(strcpy(buf, "postmaster@"),
                                             mailfrom),
                                       tcpremoteip,
                                       tcpremotehost,
                                       helodomain,
                                       mydomain,
                                       errmsg_buf,
                                       errmsg_buf_size);
              free(buf);
              return err_code;
       }

       memset(&info, 0, sizeof(info));

       info.mailfrom=mailfrom;

       /*
       ** The <current-domain> is initially drawn from the
       ** <responsible-sender>.  Recursive mechanisms such as
       ** Include and Redirect replace the initial
       ** <current-domain> with another domain.  However, they
       ** do not change the value of the <responsible-sender>.
       */
       info.current_domain=strrchr(mailfrom, '@')+1;

       info.tcpremoteip=tcpremoteip;
       info.tcpremotehost=tcpremotehost;
       info.errmsg_buf=errmsg_buf;
       info.errmsg_buf_size=errmsg_buf_size;
       info.helodomain=helodomain;
       info.mydomain=mydomain;
       info.lookup_cnt=&lookup_cnt;

       rfc1035_init_resolv(&info.res);

       result=lookup(&info);

       if (errmsg_buf[0] == 0)
       {
              static const char errmsg[]="Address %s the Sender Policy Framework";
              char *p=malloc(sizeof(errmsg)+strlen(mailfrom)+20);

              if (p)
                     sprintf(p, errmsg, result == SPF_PASS
                            ? "passes":"does not pass");

              set_err_msg(errmsg_buf, errmsg_buf_size,
                         p ? p:strerror(errno));
              if (p) free(p);
       }
       rfc1035_destroy_resolv(&info.res);
       return result;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void set_err_msg ( char *  errmsg_buf,
size_t  errmsg_buf_size,
const char *  errmsg 
) [static]

Definition at line 41 of file spf.c.

{
       size_t l=strlen(errmsg);

       if (errmsg_buf_size == 0)
              return;

       --errmsg_buf_size;

       if (l >= errmsg_buf_size)
              l=errmsg_buf_size;
       memcpy(errmsg_buf, errmsg, l);
       errmsg_buf[l]=0;
}

Here is the caller graph for this function:

static void setexp ( const char *  name,
struct rfc1035_spf_info info 
) [static]

Definition at line 959 of file spf.c.

{
       struct rfc1035_reply *reply;
       char   namebuf[RFC1035_MAXNAMESIZE+1];
       char   txtbuf[256];
       int n;
       char   *str;

       /*
       ** The argument to the explanation modifier is a domain-spec
       ** to be TXT queried.  The result of the TXT query is a
       ** macro-string that is macro-expanded.  If SPF processing
       ** results in a rejection, the expanded result SHOULD be
       ** shown to the sender in the SMTP reject message.  This
       ** string allows the publishing domain to communicate further
       ** information via the SMTP receiver to legitimate senders in
       ** the form of a short message or URL.
       */


       namebuf[0]=0;
       strncat(namebuf, exp, RFC1035_MAXNAMESIZE);

       if (rfc1035_resolve_cname(&info->res, namebuf,
                              RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
                              &reply, 0) < 0 ||
           reply == 0 ||
           (n=rfc1035_replysearch_an(&info->res,
                                  reply, namebuf, RFC1035_TYPE_TXT,
                                  RFC1035_CLASS_IN, 0)) < 0)
       {
              set_err_msg(info->errmsg_buf,
                         info->errmsg_buf_size,
                         "A DNS lookup error occured while"
                         " fetching the SPF explanation record.");
       }
       else
       {
              rfc1035_rr_gettxt(reply->allrrs[n], 0, txtbuf);

              str=expand(txtbuf, info);

              set_err_msg(info->errmsg_buf,
                         info->errmsg_buf_size,
                         str ? str:strerror(errno));
              if (str)
                     free(str);
       }
       rfc1035_replyfree(reply);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char spf_compute ( char **  words,
struct rfc1035_spf_info info 
) [static]

Definition at line 329 of file spf.c.

{
       size_t i;
       char rc;
       const char *exp=NULL;
       const char *redirect=NULL;

       for (i=0; words[i]; i++)
              if (strncasecmp(words[i], "exp=", 4) == 0)
                     exp=words[i]+4;

       for (i=0; words[i]; i++)
       {
              const char *name;
              char prefix;

              if (strncasecmp(words[i], "redirect=", 9) == 0)
                     redirect=words[i]+9;

              if (strchr(words[i], '='))
                     continue;

              name=words[i];

              switch (*name) {
              case '+':
              case '-':
              case '?':
              case '~':
                     prefix=*name++;
                     break;
              default:
                     prefix='+';
                     break;
              }

              rc=mechanism(name, info);

              /*
              ** When a mechanism is evaluated, one of three things can
              ** happen: it can match, it can not match, or it can throw an
              ** exception.
              */

              if (rc == SPF_PASS)
              {
                     /*
                     ** If it matches, processing ends and the prefix value
                     ** is returned as the result of that record.
                     */

                     if (prefix != SPF_PASS && exp)
                            setexp(exp, info);
                     return prefix;
              }

              if (rc == SPF_FAIL)
              {
                     /*
                     ** If it does not match, processing continues with
                     ** the next
                     ** mechanism.
                     */

                     continue;
              }

              /*
              ** If it throws an exception, mechanism processing ends and
              ** the exception value is returned (either "error"
              ** indicating a temporary failure, usually DNS-related, or
              ** "unknown" indicating a syntax error or other permanent
              ** failure resulting in incomplete processing.)
              */
              return rc;
       }

       if (redirect)
       {
              /*
              ** If all mechanisms fail to match, and a redirect modifier
              ** is present, then processing proceeds as follows.
              **
              ** The domain-spec portion of the redirect section is expanded
              ** as per the macro rules in section 7.  The resulting string
              ** is a new domain that is now queried:  The <current-domain>
              ** is set to this new domain, and the new domain's SPF record
              ** is fetched and processed.  Note that <responsible-sender>
              ** does not change.
              **
              ** The result of this new query is then considered the result
              ** of original query.
              */


              char *new_domain;
              struct rfc1035_spf_info newinfo;
              char rc;

              new_domain=expand(redirect, info);

              if (!new_domain)
                     return SPF_ERROR;

              newinfo= *info;
              newinfo.current_domain=new_domain;

              rc=lookup(&newinfo);
              free(new_domain);
              return rc;
       }

       /*
       ** If none of the mechanisms match and there is no redirect modifier,
       ** then the result of the SPF query is "neutral".
       */

       if (exp)
              setexp(exp, info);

       return SPF_NEUTRAL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static char * transform ( char *  macro,
unsigned  transformer_count,
char  transformer_reverse,
char  delimiter_char 
) [static]

Definition at line 1391 of file spf.c.

{
       char **words;
       unsigned n=tsplit(macro, delimiter_char, NULL);
       unsigned start;
       unsigned i;
       char *buf;
       size_t len;

       if ((words=malloc(sizeof(char *)*(n+1))) == NULL)
              return NULL;
       tsplit(macro, delimiter_char, words);
       words[n]=NULL;

       /*
       ** The DIGIT transformer indicates the number of right-hand parts to
       ** use after optional reversal.  If a DIGIT is specified, it MUST be
       ** nonzero.  If no DIGITs are specified, or if the value specifies more
       ** parts than are available, all the available parts are used.  If the
       ** DIGIT was 5, and only 3 parts were available, the macro interpreter
       ** would pretend the DIGIT was 3.  Implementations MAY limit the
       ** number, but MUST support at least a value of 9.
       */

       if (transformer_count > n || transformer_count <= 0)
              transformer_count=n;

       if (transformer_reverse)
       {
              start=0;
              n=transformer_count;
       }
       else
       {
              start=n-transformer_count;
       }

       len=1;

       for (i=start; i<n; i++)
       {
              len += strlen(words[i])+1;
       }

       buf=malloc(len);
       if (!buf)
       {
              free(words);
              return NULL;
       }

       *buf=0;
       if (transformer_reverse)
       {
              for (i=n; i>start; )
              {
                     if (*buf)
                            strcat(buf, ".");
                     strcat(buf, words[--i]);
              }
       }
       else
       {
              for (i=start; i<n; i++)
              {
                     if (*buf)
                            strcat(buf, ".");
                     strcat(buf, words[i]);
              }
       }
       free(words);
       return buf;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static unsigned tsplit ( char *  macro,
char  delimiter,
char **  wordptr 
) [static]

Definition at line 1362 of file spf.c.

{
       /* Two passes */
       unsigned cnt=0;

       if (!delimiter)
              delimiter='.';

       while (*macro)
       {
              ++cnt;

              if (wordptr)
                     *wordptr++=macro;

              while (*macro && *macro != delimiter)
                     ++macro;

              if (*macro)
              {
                     if (wordptr)
                            *macro=0;
                     ++macro;
              }

       }
       return cnt;
}             

Here is the caller graph for this function: