spf_server.c

Go to the documentation of this file.
00001 /*
00002  * This program is free software; you can redistribute it and/or modify
00003  * it under the terms of either:
00004  *
00005  *   a) The GNU Lesser General Public License as published by the Free
00006  *      Software Foundation; either version 2.1, or (at your option) any
00007  *      later version,
00008  *
00009  *   OR
00010  *
00011  *   b) The two-clause BSD license.
00012  *
00013  * These licenses can be found with the distribution in the file LICENSES
00014  */
00015 
00016 #include "spf_sys_config.h"
00017 
00018 
00019 #ifdef STDC_HEADERS
00020 # include <stdio.h>        /* stdin / stdout */
00021 # include <stdlib.h>       /* malloc / free */
00022 # include <ctype.h>        /* isupper / tolower */
00023 #endif
00024 
00025 #ifdef HAVE_INTTYPES_H
00026 #include <inttypes.h>
00027 #endif
00028 
00029 #ifdef HAVE_NETDB_H
00030 #include <netdb.h>
00031 #endif
00032 
00033 #ifdef HAVE_UNISTD_H
00034 #include <unistd.h>
00035 #endif 
00036 
00037 #ifdef HAVE_STRING_H
00038 # include <string.h>       /* strstr / strdup */
00039 #else
00040 # ifdef HAVE_STRINGS_H
00041 #  include <strings.h>       /* strstr / strdup */
00042 # endif
00043 #endif
00044 
00045 #ifdef HAVE_NETDB_H
00046 # include <netdb.h>
00047 #endif
00048 
00049 #ifndef HOST_NAME_MAX
00050 #define HOST_NAME_MAX 255
00051 #endif 
00052 
00053 
00054 #include "spf.h"
00055 #include "spf_response.h"
00056 #include "spf_record.h"
00057 #include "spf_server.h"
00058 #include "spf_dns.h"
00059 #include "spf_dns_resolv.h"
00060 #include "spf_dns_cache.h"
00061 #include "spf_dns_zone.h"
00062 #include "spf_internal.h"
00063 #include "spf_dns_internal.h"
00064 
00065 
00066 __attribute__((warn_unused_result))
00067 static SPF_errcode_t
00068 SPF_server_set_rec_dom_ghbn(SPF_server_t *sp)
00069 {
00070         sp->rec_dom = malloc(HOST_NAME_MAX);
00071         if (! sp->rec_dom)
00072                 return SPF_E_NO_MEMORY;
00073 #ifdef _WIN32
00074         gethostnameFQDN(sp->rec_dom, HOST_NAME_MAX);
00075         return 0;       /* XXX FIXME? */
00076 #else
00077         if (gethostname(sp->rec_dom, HOST_NAME_MAX) < 0)
00078                 /* XXX Error using strerror. */
00079                 return SPF_E_INTERNAL_ERROR;
00080 #endif
00081         return SPF_E_SUCCESS;
00082 }
00083 
00084 static void
00085 SPF_server_new_common_pre(SPF_server_t *sp, int debug)
00086 {
00087         SPF_errcode_t            err;
00088 
00089         memset(sp, 0, sizeof(SPF_server_t));
00090 
00091         sp->max_dns_mech = SPF_MAX_DNS_MECH;
00092         sp->max_dns_ptr = SPF_MAX_DNS_PTR;
00093         sp->max_dns_mx = SPF_MAX_DNS_MX;
00094         sp->debug = debug;
00095 
00096         err = SPF_server_set_rec_dom_ghbn(sp);
00097         if (err != SPF_E_SUCCESS)
00098                 SPF_error("Failed to set rec_dom using gethostname()");
00099 }
00100 
00101 static void
00102 SPF_server_new_common_post(SPF_server_t *sp)
00103 {
00104         SPF_response_t          *spf_response;
00105         SPF_errcode_t            err;
00106 
00107         spf_response = NULL;
00108         err = SPF_server_set_explanation(sp, SPF_DEFAULT_EXP,
00109                                         &spf_response);
00110         if (err != SPF_E_SUCCESS)
00111                 SPF_errorf("Error code %d compiling default explanation", err);
00112         if (spf_response) {
00113                 /* XXX Print the errors?! */
00114                 if (SPF_response_messages(spf_response) > 0)
00115                         SPF_error("Response errors compiling default explanation");
00116                 SPF_response_free(spf_response);
00117         }
00118 
00119         spf_response = NULL;
00120         err = SPF_server_set_localpolicy(sp, "", 0, &spf_response);
00121         if (err != SPF_E_SUCCESS)
00122                 SPF_errorf("Error code %d compiling default whitelist", err);
00123         if (spf_response) {
00124                 /* XXX Print the errors?! */
00125                 if (SPF_response_messages(spf_response) > 0)
00126                         SPF_error("Response errors compiling default whitelist");
00127                 SPF_response_free(spf_response);
00128         }
00129 }
00130 
00131 SPF_server_t *
00132 SPF_server_new(SPF_server_dnstype_t dnstype, int debug)
00133 {
00134         SPF_dns_server_t        *dc_r;
00135         SPF_dns_server_t        *dc_c;
00136         SPF_dns_server_t        *dc_z;
00137         SPF_server_t            *sp;
00138 
00139         sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
00140         if (! sp)
00141                 return sp;
00142         SPF_server_new_common_pre(sp, debug);
00143         sp->destroy_resolver = 1;
00144 
00145         switch (dnstype) {
00146                 case SPF_DNS_RESOLV:
00147                         dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
00148                         if (dc_r == NULL)
00149                                 SPF_error("Failed to create DNS resolver");
00150                         sp->resolver = dc_r;
00151                         break;
00152 
00153                 case SPF_DNS_CACHE:
00154                         dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
00155                         if (dc_r == NULL)
00156                                 SPF_error("Failed to create DNS resolver");
00157                         dc_c = SPF_dns_cache_new(dc_r, NULL, debug, 8);
00158                         if (dc_c == NULL)
00159                                 SPF_error("Failed to create DNS cache");
00160                         sp->resolver = dc_c;
00161                         break;
00162 
00163                 case SPF_DNS_ZONE:
00164                         dc_z = SPF_dns_zone_new(NULL, NULL, debug);
00165                         if (dc_z == NULL)
00166                                 SPF_error("Failed to create DNS zone");
00167                         sp->resolver = dc_z;
00168                         break;
00169 
00170                 default:
00171                         SPF_errorf("Unknown DNS type %d", dnstype);
00172         }
00173 
00174         SPF_server_new_common_post(sp);
00175 
00176         return sp;
00177 }
00178 
00179 SPF_server_t *
00180 SPF_server_new_dns(SPF_dns_server_t *dns, int debug)
00181 {
00182         SPF_server_t    *sp;
00183 
00184         sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
00185         if (! sp)
00186                 return sp;
00187         SPF_server_new_common_pre(sp, debug);
00188         sp->destroy_resolver = 0;
00189         sp->resolver = dns;
00190         SPF_server_new_common_post(sp);
00191         return sp;
00192 }
00193 
00199 void
00200 SPF_server_free(SPF_server_t *sp)
00201 {
00202         if (sp->resolver && sp->destroy_resolver)
00203                 SPF_dns_free(sp->resolver);
00204         if (sp->local_policy)
00205                 SPF_record_free(sp->local_policy);
00206         if (sp->explanation)
00207                 SPF_macro_free(sp->explanation);
00208         if (sp->rec_dom)
00209                 free(sp->rec_dom);
00210         /* XXX TODO: Free other parts of the structure. */
00211         free(sp);
00212 }
00213 
00214 SPF_errcode_t
00215 SPF_server_set_rec_dom(SPF_server_t *sp, const char *dom)
00216 {
00217         if (sp->rec_dom)
00218                 free(sp->rec_dom);
00219         if (dom == NULL)
00220                 return SPF_server_set_rec_dom_ghbn(sp);
00221         sp->rec_dom = strdup(dom);
00222         if (! sp->rec_dom)
00223                 return SPF_E_NO_MEMORY;
00224         return SPF_E_SUCCESS;
00225 }
00226 
00227 SPF_errcode_t
00228 SPF_server_set_sanitize(SPF_server_t *sp, int sanitize)
00229 {
00230         sp->sanitize = sanitize;
00231         return SPF_E_SUCCESS;
00232 }
00233 
00234 SPF_errcode_t
00235 SPF_server_set_explanation(SPF_server_t *sp, const char *exp,
00236                                 SPF_response_t **spf_responsep)
00237 {
00238         SPF_macro_t             *spf_macro = NULL;
00239         SPF_errcode_t    err;
00240 
00241         SPF_ASSERT_NOTNULL(exp);
00242 
00243         /* This is a hackish way to get the errors. */
00244         if (! *spf_responsep) {
00245                 *spf_responsep = SPF_response_new(NULL);
00246                 if (! *spf_responsep)
00247                         return SPF_E_NO_MEMORY;
00248         }
00249 
00250         err = SPF_record_compile_macro(sp, *spf_responsep, &spf_macro, exp);
00251         if (err == SPF_E_SUCCESS) {
00252                 if (sp->explanation)
00253                         SPF_macro_free(sp->explanation);
00254                 sp->explanation = spf_macro;
00255         }
00256         else {
00257                 SPF_response_add_error(*spf_responsep, err,
00258                                 "Failed to compile explanation '%s'", exp);
00259                 if (spf_macro)
00260                         SPF_macro_free(spf_macro);
00261         }
00262 
00263         return err;
00264 }
00265 
00266 SPF_errcode_t
00267 SPF_server_set_localpolicy(SPF_server_t *sp, const char *policy,
00268                                 int use_default_whitelist,
00269                                 SPF_response_t **spf_responsep)
00270 {
00271         SPF_record_t    *spf_record = NULL;
00272         SPF_errcode_t    err;
00273         char                    *record;
00274         size_t                   len;
00275 
00276         SPF_ASSERT_NOTNULL(policy);
00277 
00278         /* This is a hackish way to get the errors. */
00279         if (! *spf_responsep) {
00280                 *spf_responsep = SPF_response_new(NULL);
00281                 if (! *spf_responsep)
00282                         return SPF_E_NO_MEMORY;
00283         }
00284 
00285         len = sizeof(SPF_VER_STR) + strlen(policy) + 20;
00286         if (use_default_whitelist)
00287                 len += sizeof(SPF_DEFAULT_WHITELIST);
00288         record = malloc(len);
00289         if (! record)
00290                 return SPF_E_NO_MEMORY;
00291         if (use_default_whitelist)
00292                 snprintf(record, len, "%s %s %s",
00293                                                 SPF_VER_STR, policy, SPF_DEFAULT_WHITELIST);
00294         else
00295                 snprintf(record, len, "%s %s", SPF_VER_STR, policy);
00296 
00297         err = SPF_record_compile(sp, *spf_responsep, &spf_record, record);
00298         if (err == SPF_E_SUCCESS) {
00299                 if (sp->local_policy)
00300                         SPF_record_free(sp->local_policy);
00301                 sp->local_policy = spf_record;
00302         }
00303         else {
00304                 SPF_response_add_error(*spf_responsep, err,
00305                                 "Failed to compile local policy '%s'", policy);
00306                 if (spf_record)
00307                         SPF_record_free(spf_record);
00308         }
00309 
00310         free(record);
00311 
00312         return err;
00313 }
00314 
00315 SPF_errcode_t
00316 SPF_server_get_record(SPF_server_t *spf_server,
00317                                 SPF_request_t *spf_request,
00318                                 SPF_response_t *spf_response,
00319                                 SPF_record_t **spf_recordp)
00320 {
00321         SPF_dns_server_t                *resolver;
00322         SPF_dns_rr_t                    *rr_txt;
00323         SPF_errcode_t                    err;
00324         SPF_dns_stat_t                   herrno;
00325         const char                              *domain;
00326         ns_type                                  rr_type;
00327         int                                              num_found;
00328         int                                              idx_found;
00329         int                                              i;
00330 
00331 
00332         SPF_ASSERT_NOTNULL(spf_server);
00333         SPF_ASSERT_NOTNULL(spf_request);
00334         SPF_ASSERT_NOTNULL(spf_server->resolver);
00335         SPF_ASSERT_NOTNULL(spf_recordp);
00336 
00337         domain = spf_request->cur_dom;
00338         SPF_ASSERT_NOTNULL(domain);
00339 
00340         *spf_recordp = NULL;
00341 
00342         resolver = spf_server->resolver;
00343 
00344         if (resolver->get_spf)
00345                 return resolver->get_spf(spf_server, spf_request,
00346                                                 spf_response, spf_recordp);
00347 
00348         /* I am VERY, VERY sorry about the gotos. Shevek. */
00349         rr_type = ns_t_spf;
00350 retry:
00351         rr_txt = SPF_dns_lookup(resolver, domain, rr_type, TRUE);
00352 
00353         switch (rr_txt->herrno) {
00354                 case HOST_NOT_FOUND:
00355                         if (spf_server->debug > 0)
00356                                 SPF_debugf("get_record(%s): HOST_NOT_FOUND", domain);
00357                         SPF_dns_rr_free(rr_txt);
00358                         if (rr_type == ns_t_spf) {
00359                                 rr_type = ns_t_txt;
00360                                 goto retry;
00361                         }
00362                         spf_response->result = SPF_RESULT_NONE;
00363                         spf_response->reason = SPF_REASON_FAILURE;
00364                         return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
00365                                         "Host '%s' not found.", domain);
00366                         // break;
00367 
00368                 case NO_DATA:
00369                         if (spf_server->debug > 0)
00370                                 SPF_debugf("get_record(%s): NO_DATA", domain);
00371                         SPF_dns_rr_free(rr_txt);
00372                         if (rr_type == ns_t_spf) {
00373                                 rr_type = ns_t_txt;
00374                                 goto retry;
00375                         }
00376                         spf_response->result = SPF_RESULT_NONE;
00377                         spf_response->reason = SPF_REASON_FAILURE;
00378                         return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
00379                                         "No DNS data for '%s'.", domain);
00380                         // break;
00381 
00382                 case TRY_AGAIN:
00383                         if (spf_server->debug > 0)
00384                                 SPF_debugf("get_record(%s): TRY_AGAIN", domain);
00385                         SPF_dns_rr_free(rr_txt);
00386                         return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
00387                                         "Temporary DNS failure for '%s'.", domain);
00388                         // break;
00389 
00390                 case NO_RECOVERY:
00391                         if (spf_server->debug > 0)
00392                                 SPF_debugf("get_record(%s): NO_RECOERY", domain);
00393                         SPF_dns_rr_free(rr_txt);
00394                         return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
00395                                         "Unrecoverable DNS failure for '%s'.", domain);
00396                         // break;
00397 
00398                 case NETDB_SUCCESS:
00399                         if (spf_server->debug > 0)
00400                                 SPF_debugf("get_record(%s): NETDB_SUCCESS", domain);
00401                         break;
00402 
00403                 default:
00404                         if (spf_server->debug > 0)
00405                                 SPF_debugf("get_record(%s): UNKNOWN_ERROR", domain);
00406                         herrno = rr_txt->herrno;        // Avoid use-after-free
00407                         SPF_dns_rr_free(rr_txt);
00408                         return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
00409                                         "Unknown DNS failure for '%s': %d.",
00410                                         domain, herrno);
00411                         // break;
00412         }
00413 
00414         if (rr_txt->num_rr == 0) {
00415                 SPF_dns_rr_free(rr_txt);
00416                 if (rr_type == ns_t_spf) {
00417                         rr_type = ns_t_txt;
00418                         goto retry;
00419                 }
00420                 return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
00421                                 "No TXT records returned from DNS lookup for '%s'",
00422                                 domain);
00423         }
00424 
00425         /* Actually, this could never be used uninitialised anyway. */
00426         idx_found = 0;
00427 
00428         /* check for multiple SPF records */
00429         num_found = 0;
00430         for (i = 0; i < rr_txt->num_rr; i++) {
00431                 /*
00432                 if (spf_server->debug > 1)
00433                         SPF_debugf("Comparing '%s' with '%s'",
00434                                         SPF_VER_STR " ", rr_txt->rr[i]->txt);
00435                 */
00436                 if (strncasecmp(rr_txt->rr[i]->txt,
00437                                           SPF_VER_STR, sizeof(SPF_VER_STR) - 1) == 0) {
00438                         char    e = rr_txt->rr[i]->txt[sizeof(SPF_VER_STR) - 1];
00439                         if (e == ' ' || e == '\0') {
00440                                 if (spf_server->debug > 0)
00441                                         SPF_debugf("found SPF record: %s", rr_txt->rr[i]->txt);
00442                                 num_found++;
00443                                 idx_found = i;
00444                         }
00445                 }
00446         }
00447 
00448         if (num_found == 0) {
00449                 SPF_dns_rr_free(rr_txt);
00450                 if (rr_type == ns_t_spf) {
00451                         rr_type = ns_t_txt;
00452                         goto retry;
00453                 }
00454                 spf_response->result = SPF_RESULT_NONE;
00455                 spf_response->reason = SPF_REASON_FAILURE;
00456                 return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
00457                                 "No SPF records for '%s'", domain);
00458         }
00459         if (num_found > 1) {
00460                 SPF_dns_rr_free(rr_txt);
00461                 // rfc4408 requires permerror here.
00462                 /* XXX This could be refactored with SPF_i_done. */
00463                 spf_response->result = SPF_RESULT_PERMERROR;
00464                 spf_response->reason = SPF_REASON_FAILURE;
00465                 return SPF_response_add_error(spf_response, SPF_E_MULTIPLE_RECORDS,
00466                                 "Multiple SPF records for '%s'", domain);
00467         }
00468 
00469         /* try to compile the SPF record */
00470         err = SPF_record_compile(spf_server,
00471                                         spf_response, spf_recordp,
00472                                         rr_txt->rr[idx_found]->txt );
00473         SPF_dns_rr_free(rr_txt);
00474 
00475         /* FIXME: support multiple versions */
00476         if (err != SPF_E_SUCCESS)
00477                 return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
00478                                 "Failed to compile SPF record for '%s'", domain);
00479 
00480         return SPF_E_SUCCESS;
00481 }
00482 
00488 #define SPF_ACCESS_INT(f) \
00489         SPF_errcode_t SPF_server_set_ ## f(SPF_server_t *s, int n) { \
00490                 s->f = n; return SPF_E_SUCCESS; \
00491         } \
00492         int SPF_server_get_ ## f(SPF_server_t *s) { \
00493                 return s->f; \
00494         }
00495 
00501 SPF_ACCESS_INT(max_dns_mech);
00502 SPF_ACCESS_INT(max_dns_ptr);
00503 SPF_ACCESS_INT(max_dns_mx);

Generated on Tue Nov 4 13:27:39 2008 for libspf2 by  doxygen 1.5.4