"Fossies" - the Fresh Open Source Software Archive

Member "libspf2-1.2.10/src/libspf2/spf_server.c" (28 Jan 2012, 12332 Bytes) of package /linux/privat/libspf2-1.2.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "spf_server.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * This program is free software; you can redistribute it and/or modify
    3  * it under the terms of either:
    4  *
    5  *   a) The GNU Lesser General Public License as published by the Free
    6  *      Software Foundation; either version 2.1, or (at your option) any
    7  *      later version,
    8  *
    9  *   OR
   10  *
   11  *   b) The two-clause BSD license.
   12  *
   13  * These licenses can be found with the distribution in the file LICENSES
   14  */
   15 
   16 #include "spf_sys_config.h"
   17 
   18 
   19 #ifdef STDC_HEADERS
   20 # include <stdio.h>        /* stdin / stdout */
   21 # include <stdlib.h>       /* malloc / free */
   22 # include <ctype.h>        /* isupper / tolower */
   23 #endif
   24 
   25 #ifdef HAVE_INTTYPES_H
   26 #include <inttypes.h>
   27 #endif
   28 
   29 #ifdef HAVE_NETDB_H
   30 #include <netdb.h>
   31 #endif
   32 
   33 #ifdef HAVE_UNISTD_H
   34 #include <unistd.h>
   35 #endif 
   36 
   37 #ifdef HAVE_STRING_H
   38 # include <string.h>       /* strstr / strdup */
   39 #else
   40 # ifdef HAVE_STRINGS_H
   41 #  include <strings.h>       /* strstr / strdup */
   42 # endif
   43 #endif
   44 
   45 #ifdef HAVE_NETDB_H
   46 # include <netdb.h>
   47 #endif
   48 
   49 #ifndef HOST_NAME_MAX
   50 #define HOST_NAME_MAX 255
   51 #endif 
   52 
   53 
   54 #include "spf.h"
   55 #include "spf_response.h"
   56 #include "spf_record.h"
   57 #include "spf_server.h"
   58 #include "spf_dns.h"
   59 #include "spf_dns_resolv.h"
   60 #include "spf_dns_cache.h"
   61 #include "spf_dns_zone.h"
   62 #include "spf_internal.h"
   63 #include "spf_dns_internal.h"
   64 
   65 
   66 __attribute__((warn_unused_result))
   67 static SPF_errcode_t
   68 SPF_server_set_rec_dom_ghbn(SPF_server_t *sp)
   69 {
   70     sp->rec_dom = malloc(HOST_NAME_MAX);
   71     if (! sp->rec_dom)
   72         return SPF_E_NO_MEMORY;
   73 #ifdef _WIN32
   74     gethostnameFQDN(sp->rec_dom, HOST_NAME_MAX);
   75     return 0;   /* XXX FIXME? */
   76 #else
   77     if (gethostname(sp->rec_dom, HOST_NAME_MAX) < 0)
   78         /* XXX Error using strerror. */
   79         return SPF_E_INTERNAL_ERROR;
   80 #endif
   81     return SPF_E_SUCCESS;
   82 }
   83 
   84 static void
   85 SPF_server_new_common_pre(SPF_server_t *sp, int debug)
   86 {
   87     SPF_errcode_t        err;
   88 
   89     memset(sp, 0, sizeof(SPF_server_t));
   90 
   91     sp->max_dns_mech = SPF_MAX_DNS_MECH;
   92     sp->max_dns_ptr = SPF_MAX_DNS_PTR;
   93     sp->max_dns_mx = SPF_MAX_DNS_MX;
   94     sp->debug = debug;
   95 
   96     err = SPF_server_set_rec_dom_ghbn(sp);
   97     if (err != SPF_E_SUCCESS)
   98         SPF_error("Failed to set rec_dom using gethostname()");
   99 }
  100 
  101 static void
  102 SPF_server_new_common_post(SPF_server_t *sp)
  103 {
  104     SPF_response_t      *spf_response;
  105     SPF_errcode_t        err;
  106 
  107     spf_response = NULL;
  108     err = SPF_server_set_explanation(sp, SPF_DEFAULT_EXP,
  109                     &spf_response);
  110     if (err != SPF_E_SUCCESS)
  111         SPF_errorf("Error code %d compiling default explanation", err);
  112     if (spf_response) {
  113         /* XXX Print the errors?! */
  114         if (SPF_response_messages(spf_response) > 0)
  115             SPF_error("Response errors compiling default explanation");
  116         SPF_response_free(spf_response);
  117     }
  118 
  119     spf_response = NULL;
  120     err = SPF_server_set_localpolicy(sp, "", 0, &spf_response);
  121     if (err != SPF_E_SUCCESS)
  122         SPF_errorf("Error code %d compiling default whitelist", err);
  123     if (spf_response) {
  124         /* XXX Print the errors?! */
  125         if (SPF_response_messages(spf_response) > 0)
  126             SPF_error("Response errors compiling default whitelist");
  127         SPF_response_free(spf_response);
  128     }
  129 }
  130 
  131 SPF_server_t *
  132 SPF_server_new(SPF_server_dnstype_t dnstype, int debug)
  133 {
  134     SPF_dns_server_t    *dc_r;
  135     SPF_dns_server_t    *dc_c;
  136     SPF_dns_server_t    *dc_z;
  137     SPF_server_t        *sp;
  138 
  139     sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
  140     if (! sp)
  141         return sp;
  142     SPF_server_new_common_pre(sp, debug);
  143     sp->destroy_resolver = 1;
  144 
  145     switch (dnstype) {
  146         case SPF_DNS_RESOLV:
  147             dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
  148             if (dc_r == NULL)
  149                 SPF_error("Failed to create DNS resolver");
  150             sp->resolver = dc_r;
  151             break;
  152 
  153         case SPF_DNS_CACHE:
  154             dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
  155             if (dc_r == NULL)
  156                 SPF_error("Failed to create DNS resolver");
  157             dc_c = SPF_dns_cache_new(dc_r, NULL, debug, 8);
  158             if (dc_c == NULL)
  159                 SPF_error("Failed to create DNS cache");
  160             sp->resolver = dc_c;
  161             break;
  162 
  163         case SPF_DNS_ZONE:
  164             dc_z = SPF_dns_zone_new(NULL, NULL, debug);
  165             if (dc_z == NULL)
  166                 SPF_error("Failed to create DNS zone");
  167             sp->resolver = dc_z;
  168             break;
  169 
  170         default:
  171             SPF_errorf("Unknown DNS type %d", dnstype);
  172     }
  173 
  174     SPF_server_new_common_post(sp);
  175 
  176     return sp;
  177 }
  178 
  179 SPF_server_t *
  180 SPF_server_new_dns(SPF_dns_server_t *dns, int debug)
  181 {
  182     SPF_server_t    *sp;
  183 
  184     sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
  185     if (! sp)
  186         return sp;
  187     SPF_server_new_common_pre(sp, debug);
  188     sp->destroy_resolver = 0;
  189     sp->resolver = dns;
  190     SPF_server_new_common_post(sp);
  191     return sp;
  192 }
  193 
  194 /**
  195  * This function destroys the DNS layer as well.
  196  * If the (custom) DNS layer has no destructor,
  197  * then this cannot and does not destroy it.
  198  */
  199 void
  200 SPF_server_free(SPF_server_t *sp)
  201 {
  202     if (sp->resolver && sp->destroy_resolver)
  203         SPF_dns_free(sp->resolver);
  204     if (sp->local_policy)
  205         SPF_record_free(sp->local_policy);
  206     if (sp->explanation)
  207         SPF_macro_free(sp->explanation);
  208     if (sp->rec_dom)
  209         free(sp->rec_dom);
  210     /* XXX TODO: Free other parts of the structure. */
  211     free(sp);
  212 }
  213 
  214 SPF_errcode_t
  215 SPF_server_set_rec_dom(SPF_server_t *sp, const char *dom)
  216 {
  217     if (sp->rec_dom)
  218         free(sp->rec_dom);
  219     if (dom == NULL)
  220         return SPF_server_set_rec_dom_ghbn(sp);
  221     sp->rec_dom = strdup(dom);
  222     if (! sp->rec_dom)
  223         return SPF_E_NO_MEMORY;
  224     return SPF_E_SUCCESS;
  225 }
  226 
  227 SPF_errcode_t
  228 SPF_server_set_sanitize(SPF_server_t *sp, int sanitize)
  229 {
  230     sp->sanitize = sanitize;
  231     return SPF_E_SUCCESS;
  232 }
  233 
  234 SPF_errcode_t
  235 SPF_server_set_explanation(SPF_server_t *sp, const char *exp,
  236                 SPF_response_t **spf_responsep)
  237 {
  238     SPF_macro_t     *spf_macro = NULL;
  239     SPF_errcode_t    err;
  240 
  241     SPF_ASSERT_NOTNULL(exp);
  242 
  243     /* This is a hackish way to get the errors. */
  244     if (! *spf_responsep) {
  245         *spf_responsep = SPF_response_new(NULL);
  246         if (! *spf_responsep)
  247             return SPF_E_NO_MEMORY;
  248     }
  249 
  250     err = SPF_record_compile_macro(sp, *spf_responsep, &spf_macro, exp);
  251     if (err == SPF_E_SUCCESS) {
  252         if (sp->explanation)
  253             SPF_macro_free(sp->explanation);
  254         sp->explanation = spf_macro;
  255     }
  256     else {
  257         SPF_response_add_error(*spf_responsep, err,
  258                 "Failed to compile explanation '%s'", exp);
  259         if (spf_macro)
  260             SPF_macro_free(spf_macro);
  261     }
  262 
  263     return err;
  264 }
  265 
  266 SPF_errcode_t
  267 SPF_server_set_localpolicy(SPF_server_t *sp, const char *policy,
  268                 int use_default_whitelist,
  269                 SPF_response_t **spf_responsep)
  270 {
  271     SPF_record_t    *spf_record = NULL;
  272     SPF_errcode_t    err;
  273     char            *record;
  274     size_t           len;
  275 
  276     SPF_ASSERT_NOTNULL(policy);
  277 
  278     /* This is a hackish way to get the errors. */
  279     if (! *spf_responsep) {
  280         *spf_responsep = SPF_response_new(NULL);
  281         if (! *spf_responsep)
  282             return SPF_E_NO_MEMORY;
  283     }
  284 
  285     len = sizeof(SPF_VER_STR) + strlen(policy) + 20;
  286     if (use_default_whitelist)
  287         len += sizeof(SPF_DEFAULT_WHITELIST);
  288     record = malloc(len);
  289     if (! record)
  290         return SPF_E_NO_MEMORY;
  291     if (use_default_whitelist)
  292         snprintf(record, len, "%s %s %s",
  293                         SPF_VER_STR, policy, SPF_DEFAULT_WHITELIST);
  294     else
  295         snprintf(record, len, "%s %s", SPF_VER_STR, policy);
  296 
  297     err = SPF_record_compile(sp, *spf_responsep, &spf_record, record);
  298     if (err == SPF_E_SUCCESS) {
  299         if (sp->local_policy)
  300             SPF_record_free(sp->local_policy);
  301         sp->local_policy = spf_record;
  302     }
  303     else {
  304         SPF_response_add_error(*spf_responsep, err,
  305                 "Failed to compile local policy '%s'", policy);
  306         if (spf_record)
  307             SPF_record_free(spf_record);
  308     }
  309 
  310     free(record);
  311 
  312     return err;
  313 }
  314 
  315 SPF_errcode_t
  316 SPF_server_get_record(SPF_server_t *spf_server,
  317                 SPF_request_t *spf_request,
  318                 SPF_response_t *spf_response,
  319                 SPF_record_t **spf_recordp)
  320 {
  321     SPF_dns_server_t        *resolver;
  322     SPF_dns_rr_t            *rr_txt;
  323     SPF_errcode_t            err;
  324     SPF_dns_stat_t           herrno;
  325     const char              *domain;
  326     ns_type                  rr_type;
  327     int                      num_found;
  328     int                      idx_found;
  329     int                      i;
  330 
  331 
  332     SPF_ASSERT_NOTNULL(spf_server);
  333     SPF_ASSERT_NOTNULL(spf_request);
  334     SPF_ASSERT_NOTNULL(spf_server->resolver);
  335     SPF_ASSERT_NOTNULL(spf_recordp);
  336 
  337     domain = spf_request->cur_dom;
  338     SPF_ASSERT_NOTNULL(domain);
  339 
  340     *spf_recordp = NULL;
  341 
  342     resolver = spf_server->resolver;
  343 
  344     if (resolver->get_spf)
  345         return resolver->get_spf(spf_server, spf_request,
  346                         spf_response, spf_recordp);
  347 
  348     /* I am VERY, VERY sorry about the gotos. Shevek. */
  349     rr_type = ns_t_spf;
  350 retry:
  351     rr_txt = SPF_dns_lookup(resolver, domain, rr_type, TRUE);
  352 
  353     switch (rr_txt->herrno) {
  354         case HOST_NOT_FOUND:
  355             if (spf_server->debug > 0)
  356                 SPF_debugf("get_record(%s): HOST_NOT_FOUND", domain);
  357             SPF_dns_rr_free(rr_txt);
  358             if (rr_type == ns_t_spf) {
  359                 rr_type = ns_t_txt;
  360                 goto retry;
  361             }
  362             spf_response->result = SPF_RESULT_NONE;
  363             spf_response->reason = SPF_REASON_FAILURE;
  364             return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
  365                     "Host '%s' not found.", domain);
  366             // break;
  367 
  368         case NO_DATA:
  369             if (spf_server->debug > 0)
  370                 SPF_debugf("get_record(%s): NO_DATA", domain);
  371             SPF_dns_rr_free(rr_txt);
  372             if (rr_type == ns_t_spf) {
  373                 rr_type = ns_t_txt;
  374                 goto retry;
  375             }
  376             spf_response->result = SPF_RESULT_NONE;
  377             spf_response->reason = SPF_REASON_FAILURE;
  378             return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
  379                     "No DNS data for '%s'.", domain);
  380             // break;
  381 
  382         case TRY_AGAIN:
  383             if (spf_server->debug > 0)
  384                 SPF_debugf("get_record(%s): TRY_AGAIN", domain);
  385             SPF_dns_rr_free(rr_txt);
  386             return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
  387                     "Temporary DNS failure for '%s'.", domain);
  388             // break;
  389 
  390         case NO_RECOVERY:
  391             if (spf_server->debug > 0)
  392                 SPF_debugf("get_record(%s): NO_RECOERY", domain);
  393             SPF_dns_rr_free(rr_txt);
  394             return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
  395                     "Unrecoverable DNS failure for '%s'.", domain);
  396             // break;
  397 
  398         case NETDB_SUCCESS:
  399             if (spf_server->debug > 0)
  400                 SPF_debugf("get_record(%s): NETDB_SUCCESS", domain);
  401             break;
  402 
  403         default:
  404             if (spf_server->debug > 0)
  405                 SPF_debugf("get_record(%s): UNKNOWN_ERROR", domain);
  406             herrno = rr_txt->herrno;    // Avoid use-after-free
  407             SPF_dns_rr_free(rr_txt);
  408             return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
  409                     "Unknown DNS failure for '%s': %d.",
  410                     domain, herrno);
  411             // break;
  412     }
  413 
  414     if (rr_txt->num_rr == 0) {
  415         SPF_dns_rr_free(rr_txt);
  416         if (rr_type == ns_t_spf) {
  417             rr_type = ns_t_txt;
  418             goto retry;
  419         }
  420         return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
  421                 "No TXT records returned from DNS lookup for '%s'",
  422                 domain);
  423     }
  424 
  425     /* Actually, this could never be used uninitialised anyway. */
  426     idx_found = 0;
  427 
  428     /* check for multiple SPF records */
  429     num_found = 0;
  430     for (i = 0; i < rr_txt->num_rr; i++) {
  431         /*
  432         if (spf_server->debug > 1)
  433             SPF_debugf("Comparing '%s' with '%s'",
  434                     SPF_VER_STR " ", rr_txt->rr[i]->txt);
  435         */
  436         if (strncasecmp(rr_txt->rr[i]->txt,
  437                       SPF_VER_STR, sizeof(SPF_VER_STR) - 1) == 0) {
  438             char    e = rr_txt->rr[i]->txt[sizeof(SPF_VER_STR) - 1];
  439             if (e == ' ' || e == '\0') {
  440                 if (spf_server->debug > 0)
  441                     SPF_debugf("found SPF record: %s", rr_txt->rr[i]->txt);
  442                 num_found++;
  443                 idx_found = i;
  444             }
  445         }
  446     }
  447 
  448     if (num_found == 0) {
  449         SPF_dns_rr_free(rr_txt);
  450         if (rr_type == ns_t_spf) {
  451             rr_type = ns_t_txt;
  452             goto retry;
  453         }
  454         spf_response->result = SPF_RESULT_NONE;
  455         spf_response->reason = SPF_REASON_FAILURE;
  456         return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
  457                 "No SPF records for '%s'", domain);
  458     }
  459     if (num_found > 1) {
  460         SPF_dns_rr_free(rr_txt);
  461         // rfc4408 requires permerror here.
  462         /* XXX This could be refactored with SPF_i_done. */
  463         spf_response->result = SPF_RESULT_PERMERROR;
  464         spf_response->reason = SPF_REASON_FAILURE;
  465         return SPF_response_add_error(spf_response, SPF_E_MULTIPLE_RECORDS,
  466                 "Multiple SPF records for '%s'", domain);
  467     }
  468 
  469     /* try to compile the SPF record */
  470     err = SPF_record_compile(spf_server,
  471                     spf_response, spf_recordp,
  472                     rr_txt->rr[idx_found]->txt );
  473     SPF_dns_rr_free(rr_txt);
  474 
  475     /* FIXME: support multiple versions */
  476     if (err != SPF_E_SUCCESS)
  477         return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
  478                 "Failed to compile SPF record for '%s'", domain);
  479 
  480     return SPF_E_SUCCESS;
  481 }
  482 
  483 /**
  484  * Various accessors.
  485  *
  486  * The user is permitted to override the maximums.
  487  */
  488 #define SPF_ACCESS_INT(f) \
  489     SPF_errcode_t SPF_server_set_ ## f(SPF_server_t *s, int n) { \
  490         s->f = n; return SPF_E_SUCCESS; \
  491     } \
  492     int SPF_server_get_ ## f(SPF_server_t *s) { \
  493         return s->f; \
  494     }
  495 
  496 /**
  497  * The return values from these getter functions are used without
  498  * modification. If you set a value higher than the specified
  499  * maximum, it will be used. Beware.
  500  */
  501 SPF_ACCESS_INT(max_dns_mech);
  502 SPF_ACCESS_INT(max_dns_ptr);
  503 SPF_ACCESS_INT(max_dns_mx);