/* * This program is free software; you can redistribute it and/or modify * it under the terms of either: * * a) The GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1, or (at your option) any * later version, * * OR * * b) The two-clause BSD license. * * These licenses can be found with the distribution in the file LICENSES */ #include "spf_sys_config.h" #ifdef STDC_HEADERS # include /* stdin / stdout */ # include /* malloc / free */ #endif #ifdef HAVE_STRING_H # include /* strstr / strdup */ #else # ifdef HAVE_STRINGS_H # include /* strstr / strdup */ # endif #endif #include "spf.h" #include "spf_dns.h" #include "spf_request.h" #include "spf_internal.h" #define SPF_FREE(x) \ do { if (x) free(x); (x) = NULL; } while(0) SPF_request_t * SPF_request_new(SPF_server_t *spf_server) { SPF_request_t *sr; sr = (SPF_request_t *)malloc(sizeof(SPF_request_t)); if (! sr) return sr; memset(sr, 0, sizeof(SPF_request_t)); sr->spf_server = spf_server; sr->client_ver = AF_UNSPEC; sr->ipv4.s_addr = htonl(INADDR_ANY); sr->ipv6 = in6addr_any; return sr; } void SPF_request_free(SPF_request_t *sr) { SPF_ASSERT_NOTNULL(sr); SPF_FREE(sr->client_dom); SPF_FREE(sr->helo_dom); SPF_FREE(sr->env_from); SPF_FREE(sr->env_from_lp); SPF_FREE(sr->env_from_dp); free(sr); } SPF_errcode_t SPF_request_set_ipv4(SPF_request_t *sr, struct in_addr addr) { if (sr->client_dom) { free(sr->client_dom); sr->client_dom = NULL; } sr->client_ver = AF_INET; sr->ipv4 = addr; return SPF_E_SUCCESS; } SPF_errcode_t SPF_request_set_ipv6(SPF_request_t *sr, struct in6_addr addr) { if (sr->client_dom) { free(sr->client_dom); sr->client_dom = NULL; } sr->client_ver = AF_INET6; sr->ipv6 = addr; return SPF_E_SUCCESS; } SPF_errcode_t SPF_request_set_ipv4_str(SPF_request_t *sr, const char *astr) { struct in_addr addr; if (astr == NULL) astr = "0.0.0.0"; if (inet_pton(AF_INET, astr, &addr) <= 0) return SPF_E_INVALID_IP4; return SPF_request_set_ipv4(sr, addr); } SPF_errcode_t SPF_request_set_ipv6_str(SPF_request_t *sr, const char *astr) { struct in6_addr addr; if (astr == NULL) astr = "::"; if (inet_pton(AF_INET6, astr, &addr) <= 0) return SPF_E_INVALID_IP6; return SPF_request_set_ipv6(sr, addr); } SPF_errcode_t SPF_request_set_helo_dom(SPF_request_t *sr, const char *dom) { SPF_ASSERT_NOTNULL(dom); SPF_FREE(sr->helo_dom); sr->helo_dom = strdup(dom); if (! sr->helo_dom) return SPF_E_NO_MEMORY; /* set cur_dom and env_from? */ if (sr->env_from == NULL) return SPF_request_set_env_from(sr, dom); return SPF_E_SUCCESS; } const char * SPF_request_get_rec_dom(SPF_request_t *sr) { SPF_server_t *spf_server; spf_server = sr->spf_server; return spf_server->rec_dom; } int SPF_request_set_env_from(SPF_request_t *sr, const char *from) { char *cp; size_t len; SPF_ASSERT_NOTNULL(from); SPF_FREE(sr->env_from); SPF_FREE(sr->env_from_lp); SPF_FREE(sr->env_from_dp); if (*from == '\0' && sr->helo_dom != NULL) from = sr->helo_dom; cp = strrchr(from, '@'); if (cp && (cp != from)) { sr->env_from = strdup(from); if (! sr->env_from) return SPF_E_NO_MEMORY; len = cp - from; sr->env_from_lp = malloc(len + 1); if (!sr->env_from_lp) { SPF_FREE(sr->env_from); return SPF_E_NO_MEMORY; } strncpy(sr->env_from_lp, from, len); sr->env_from_lp[len] = '\0'; sr->env_from_dp = strdup(cp + 1); if (!sr->env_from_dp) { SPF_FREE(sr->env_from); SPF_FREE(sr->env_from_lp); return SPF_E_NO_MEMORY; } } else { if (cp == from) from++; /* "@domain.example" */ len = sizeof("postmaster@") + strlen(from); sr->env_from = malloc(len + 1); /* sizeof("") == 1? */ if (! sr->env_from) return SPF_E_NO_MEMORY; sprintf(sr->env_from, "postmaster@%s", from); sr->env_from_lp = strdup("postmaster"); if (!sr->env_from_lp) { SPF_FREE(sr->env_from); return SPF_E_NO_MEMORY; } sr->env_from_dp = strdup(from); if (!sr->env_from_dp) { SPF_FREE(sr->env_from); SPF_FREE(sr->env_from_lp); return SPF_E_NO_MEMORY; } } return 0; // SPF_E_SUCCESS } const char * SPF_request_get_client_dom(SPF_request_t *sr) { SPF_server_t *spf_server; SPF_ASSERT_NOTNULL(sr); spf_server = sr->spf_server; SPF_ASSERT_NOTNULL(spf_server); if (sr->client_dom == NULL) { sr->client_dom = SPF_dns_get_client_dom(spf_server->resolver, sr); } return sr->client_dom; } int SPF_request_is_loopback(SPF_request_t *sr) { if (sr->client_ver == AF_INET) { if ((ntohl(sr->ipv4.s_addr) & IN_CLASSA_NET) == (IN_LOOPBACKNET << 24)) { return TRUE; } } else if (sr->client_ver == AF_INET6) { if (IN6_IS_ADDR_LOOPBACK(&sr->ipv6)) return TRUE; } return FALSE; } static SPF_errcode_t SPF_request_prepare(SPF_request_t *sr) { if (sr->use_helo) sr->cur_dom = sr->helo_dom; else sr->cur_dom = sr->env_from_dp; return SPF_E_SUCCESS; } /** * The common tail-end of a few methods below. */ static SPF_errcode_t SPF_request_query_record(SPF_request_t *spf_request, SPF_response_t *spf_response, SPF_record_t *spf_record, SPF_errcode_t err) { if (err != SPF_E_SUCCESS) { if (spf_record) SPF_record_free(spf_record); return err; } /* Now, in theory, SPF_response_errors(spf_response) == 0 */ if (SPF_response_errors(spf_response) > 0) SPF_infof("Warning: %d errors in response, " "but no error code. Evaluating.", SPF_response_errors(spf_response)); /* If we get here, spf_record better not be NULL */ spf_response->spf_record_exp = spf_record; err = SPF_record_interpret(spf_record, spf_request, spf_response, 0); SPF_record_free(spf_record); return err; } /** * The big entry point. */ SPF_errcode_t SPF_request_query_mailfrom(SPF_request_t *spf_request, SPF_response_t **spf_responsep) { SPF_server_t *spf_server; SPF_record_t *spf_record; SPF_errcode_t err; SPF_ASSERT_NOTNULL(spf_request); spf_server = spf_request->spf_server; SPF_ASSERT_NOTNULL(spf_server); *spf_responsep = SPF_response_new(spf_request); if (! *spf_responsep) return SPF_E_NO_MEMORY; /* Give localhost a free ride */ if (SPF_request_is_loopback(spf_request)) return SPF_i_done(*spf_responsep, SPF_RESULT_PASS, SPF_REASON_LOCALHOST, SPF_E_SUCCESS); SPF_request_prepare(spf_request); err = SPF_server_get_record(spf_server, spf_request, *spf_responsep, &spf_record); return SPF_request_query_record(spf_request, *spf_responsep, spf_record, err); } /* This interface isn't finalised. */ SPF_errcode_t SPF_request_query_fallback(SPF_request_t *spf_request, SPF_response_t **spf_responsep, const char *record) { SPF_server_t *spf_server; SPF_record_t *spf_record; SPF_errcode_t err; SPF_ASSERT_NOTNULL(spf_request); spf_server = spf_request->spf_server; SPF_ASSERT_NOTNULL(spf_server); *spf_responsep = SPF_response_new(spf_request); if (! *spf_responsep) return SPF_E_NO_MEMORY; /* Give localhost a free ride */ if (SPF_request_is_loopback(spf_request)) return SPF_i_done(*spf_responsep, SPF_RESULT_PASS, SPF_REASON_LOCALHOST, SPF_E_SUCCESS); SPF_request_prepare(spf_request); err = SPF_record_compile(spf_server, *spf_responsep, &spf_record, record); return SPF_request_query_record(spf_request, *spf_responsep, spf_record, err); } /** * This replaces _2mx * * build record as SPF_VER_STR " mx:%s" * Set cur_dom to the rcpt_to domain. * Query on the 'fixed' 2mx record. * Clobber the primary result. */ /* FIXME: Check the implementation of this. */ SPF_errcode_t SPF_request_query_rcptto(SPF_request_t *spf_request, SPF_response_t **spf_responsep, const char *rcpt_to) { SPF_server_t *spf_server; SPF_record_t *spf_record; SPF_errcode_t err; const char *rcpt_to_dom; char *record; size_t len; if ( (spf_request == NULL) || (rcpt_to == NULL) ) return SPF_E_INVALID_OPT; SPF_ASSERT_NOTNULL(spf_request); spf_server = spf_request->spf_server; SPF_ASSERT_NOTNULL(spf_server); *spf_responsep = SPF_response_new(spf_request); if (! *spf_responsep) return SPF_E_NO_MEMORY; /* Give localhost a free ride */ if (SPF_request_is_loopback(spf_request)) return SPF_i_done(*spf_responsep, SPF_RESULT_PASS, SPF_REASON_LOCALHOST, SPF_E_SUCCESS); rcpt_to_dom = strchr(rcpt_to, '@'); if (rcpt_to_dom == NULL) rcpt_to_dom = rcpt_to; spf_request->cur_dom = rcpt_to_dom; len = sizeof(SPF_VER_STR) + 64 + strlen(rcpt_to_dom); record = malloc(len); if (! record) return SPF_E_NO_MEMORY; snprintf(record, len, SPF_VER_STR " mx:%s", rcpt_to_dom); err = SPF_record_compile(spf_server, *spf_responsep, &spf_record, record); free(record); return SPF_request_query_record(spf_request, *spf_responsep, spf_record, err); }