ShellBanner
System:Linux MiraNet 3.0.0-14-generic-pae #23-Ubuntu SMP Mon Nov 21 22:07:10 UTC 2011 i686
Software:Apache. PHP/5.3.6-13ubuntu3.10
ID:uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
Safe Mode:OFF
Open_Basedir:OFF
Freespace:25.76 GB of 70.42 GB (36.59%)
MySQL: ON MSSQL: OFF Oracle: OFF PostgreSQL: OFF Curl: OFF Sockets: ON Fetch: OFF Wget: ON Perl: ON
Disabled Functions: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,

/ usr/ src/ courier-0.66.1/ rfc1035/ - drwxrwxrwx

Directory:
Viewing file:     spf.c (28.9 KB)      -rw-rw-rw-
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
** Copyright 2004-2006 Double Precision, Inc.
** See COPYING for distribution information.
*/

#include    "config.h"
#include    "spf.h"
#include    "rfc1035_res.h"
#include    "rfc1035mxlist.h"
#include    <stdio.h>
#include    <ctype.h>
#include    <stdlib.h>
#include    <string.h>
#include    <errno.h>
#include    <sys/types.h>
#if TIME_WITH_SYS_TIME
#include    <sys/time.h>
#include    <time.h>
#else
#if HAVE_SYS_TIME_H
#include    <sys/time.h>
#else
#include    <time.h>
#endif
#endif


static void set_err_msg(char *errmsg_buf,
            size_t errmsg_buf_size,
            const char *errmsg)
{
    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;
}

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)
{
    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;
    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);
    }
    return result;
}

static int isspf1(struct rfc1035_reply *reply, int n)
{
    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;
}

char rfc1035_spf_gettxt(const char *current_domain,
            char *buf)
{
    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(&rfc1035_default_resolver,
                  RFC1035_RESOLVE_RECURSIVE, namebuf,
                  RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
                  &reply, 0) < 0 ||
        reply == 0 ||
        (n=rfc1035_replysearch_an(&rfc1035_default_resolver,
                      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(&rfc1035_default_resolver,
                     reply, namebuf,
                     RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
                     n+1);
    }

    if (n >= 0)
    {
        for (o=n; (o=rfc1035_replysearch_an(&rfc1035_default_resolver,
                            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;
}

/*
** Chop up an SPF record into whitespace-delimited words.
** get_words() is called twice: once with wordptr=NULL - return # of words,
** second time with wordptr!=NULL, parse the words.
*/

static unsigned get_words(char *record, char **wordptr)
{
    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;
}

static char spf_compute(char **words,
            struct rfc1035_spf_info *info);

char rfc1035_spf_compute(char *record,
             struct rfc1035_spf_info *info)
{
    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;
}

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 char spf_compute(char **words,
            struct rfc1035_spf_info *info)
{
    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;
}

static int get_dual_cidr_length(const char *p)
{
#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
}

static int ip_compare(const RFC1035_ADDR *a,
              const RFC1035_ADDR *b,
              int pfix)
{
    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;
}

static void get_domain_pfix(struct rfc1035_spf_info *info,
                const char *start,
                char **domain_ptr,
                int  *pfix_ptr)
{
    *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));
}

struct ptr_info {
    const char *name;
    struct rfc1035_spf_info *info;
    RFC1035_ADDR addr;
    int found;
    int error;
};

static void check_ptr(const char *ptr, void *void_arg)
{
    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(&rfc1035_default_resolver, 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);
}

static char do_ptr(const char *name,
           struct rfc1035_spf_info *info)
{
    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(&rfc1035_default_resolver, &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;
}

static char do_ipcheck(const char *name, struct rfc1035_spf_info *info,
               int pfix_add)
{
    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;
}

static char mechanism(const char *name,
              struct rfc1035_spf_info *info)
{
    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(&rfc1035_default_resolver,
                 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(&rfc1035_default_resolver,
                       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(&rfc1035_default_resolver,
                 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;
}

static void setexp(const char *exp,
           struct rfc1035_spf_info *info)
{
    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(&rfc1035_default_resolver,
                  RFC1035_RESOLVE_RECURSIVE, namebuf,
                  RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
                  &reply, 0) < 0 ||
        reply == 0 ||
        (n=rfc1035_replysearch_an(&rfc1035_default_resolver,
                      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);
}

static char lookup(struct rfc1035_spf_info *info)
{
    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);
}

/*
**
** Certain directives perform macro interpolation on their arguments.
**
** Two passes: count # of chars in the expanded macro, generate the macro.
*/

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)
{
    *(size_t *)va += n;
}

static void do_save(const char *p, size_t n, void *va)
{
    char **b=(char **)va;

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

static char *expand(const char *str,
            struct rfc1035_spf_info *info)
{
    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;
}

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)
{
    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;
}

static char *expandc(const char *ipaddr);
static char *expandi(const char *ipaddr);

static char *get_macro(struct rfc1035_spf_info *info, char name)
{
    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("");
}

/*
**
**   For IPv4 addresses, both the "i" and "c" macros expand to the
**   standard dotted-quad format.
**
**   For IPv6 addresses, the "i" macro expands to dot-format address; it
**   is intended for use in %{ir}.  The "c" macro may expand to any of
**   the hexadecimal colon-format addresses specified in [RFC3513] section
**   2.2.  It is intended for humans to read.
*/

static char *expandc(const char *ipaddr)
{
    if (strncmp(ipaddr, "::ffff:", 7) == 0)
        return strdup(ipaddr+7);
    return strdup(ipaddr);
}

static char *expandi(const char *ipaddr)
{
    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);
}

/*
**   If transformers or delimiters are provided, the macro strings are
**   split into parts.  After performing any reversal operation or
**   removal of left-hand parts, the parts are rejoined using "." and not
**   the original splitting characters.
*/

static unsigned tsplit(char *macro, char delimiter, char **wordptr)
{
    /* 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;
}        

static char *transform(char *macro,
               unsigned transformer_count,
               char transformer_reverse,
               char delimiter_char)
{
    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;
}
Command:
Quick Commands:
Upload:
[OK] Max size: 100MB
PHP Filesystem: <@ Ú
Search File:
regexp
Create File:
Overwrite [OK]
View File:
Mass Defacement:
[+] Main Directory: [+] Defacement Url:
LmfaoX Shell - Private Build [BETA] - v0.1 -; Generated: 0.2292 seconds