/* * spf_example - An example program for how to use libspf2 * * Author: Wayne Schlitt * * File: spfquery.c * Desc: SPF command line utility * * * This program is in the public domain, there is no copyright, you * can do anything you want with it. */ /* * The libspf2 library uses the GNU autoconf system to help make * the library more portable. The config.h file should have the * HAVE_xxx defines that are appropriate for your system. Either use * autconf to create it, or create it by hand. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef STDC_HEADERS # include # include /* malloc / free */ #endif #ifdef HAVE_SYS_TYPES_H #include /* types (u_char .. etc..) */ #endif #ifdef HAVE_INTTYPES_H #include #endif #ifdef HAVE_STRING_H # include /* strstr / strdup */ #else # ifdef HAVE_STRINGS_H # include /* strstr / strdup */ # endif #endif #ifdef HAVE_SYS_SOCKET_H # include /* inet_ functions / structs */ #endif #ifdef HAVE_NETINET_IN_H # include /* inet_ functions / structs */ #endif #ifdef HAVE_ARPA_INET_H # include /* in_addr struct */ #endif #ifdef HAVE_ARPA_NAMESER_H # include /* DNS HEADER struct */ #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_GETOPT_H #include #endif /* * libspf2 public include files that are needed for this example * program */ #include "spf.h" /* * usage() just prints out the command line options for this program */ static void usage() { fprintf( stderr, "Usage:\n" "\n" "spf_example [options]\n" "\n" "Valid data options are:\n" " -i The IP address that is sending email\n" " -s The email address used as the\n" " envelope-from. If no username (local\n" " part) is given, 'postmaster' will be\n" " assumed.\n" " -r [optional] The email address used as\n" " the envelope-to email address, for\n" " secondary-MX checking.\n" " -h The domain name given on the SMTP HELO\n" " command. This is only needed if the\n" " -sender option is not given.\n" " -d [debug level] debug level.\n" ); } /* * All the code is in the main routine, but most usages of libspf2 * would have the code spread around into various subrotines. */ int main( int argc, char *argv[] ) { int c; int res = 0; int i; char *opt_ip = NULL; char *opt_sender = NULL; char *opt_helo = NULL; char *opt_rcpt_to = NULL; int opt_debug = 0; /* You should not indirect on any of these structures, as their * layout may change between versions of the library. Use the * accessor functions instead. Definitions of the structs may not * even be provided. */ SPF_server_t *spf_server = NULL; SPF_request_t *spf_request = NULL; SPF_response_t *spf_response = NULL; SPF_response_t *spf_response_2mx = NULL; /* * check the arguments */ while (1) { c = getopt(argc, argv, "i:s:h:r:d::" ); if (c == -1) break; switch (c) { case 'i': opt_ip = optarg; break; case 's': opt_sender = optarg; break; case 'h': opt_helo = optarg; break; case 'r': opt_rcpt_to = optarg; break; case 0: case '?': usage(); res = 255; goto error; break; case 'd': if (optarg == NULL) opt_debug = 1; else opt_debug = atoi( optarg ); break; default: fprintf( stderr, "Error: getopt returned character code 0%o ??\n", c); } } if (optind != argc || opt_ip == NULL || (opt_helo == NULL && opt_sender == NULL)) { usage(); res = 255; goto error; } /* * Configure the SPF system. * * libspf2 is designed so that configurations can be set up once * and reused many times different emails delivered in a single SMTP * session or in different SMTP sessions. */ /* * set up the SPF server * * Configurations contain malloc'd data so must be * destroyed when you are finished. */ spf_server = SPF_server_new(SPF_DNS_CACHE, opt_debug); if (spf_server == NULL) { fprintf( stderr, "SPF_create_config failed.\n" ); res = 255; goto error; } /* * Create a new request. * * The SPF request contains all the data needed to process * the SPF check. Requests are malloc'd so it must be * destroyed when you are finished with it. */ spf_request = SPF_request_new(spf_server); /* The domain name of the receiving MTA will default to gethostname() */ /* SPF_request_set_rec_dom( spf_request, opt_name ); */ /* * process the SPF request * * Now that the SPF system has been configured, we can process the requests. * There would normally be a loop around this code or it would be placed * in a subroutine to be called for each email. * * If a single email session sends several emails, you don't need to * reset the IP address or the HELO domain each time, just change the * envelope from. */ /* * record the IP address of the client (sending) MTA. * * There are other SPF_set_ip*() functionx if you have a structure * instead of a string. */ if ( SPF_request_set_ipv4_str( spf_request, opt_ip ) ) { printf( "Invalid IP address.\n" ); res = 255; goto error; } /* * record the HELO domain name of the client (sending) MTA from * the SMTP HELO or EHLO commands * * This domain name will be used if the envelope from address is * null (e.g. MAIL FROM:<>). This happens when a bounce is being * sent and, in effect, it is the client MTA that is sending the * message. */ if (opt_helo == NULL) { if (opt_sender != NULL) { if (strstr(opt_sender, "@") != NULL) { opt_helo = strdup(strstr(opt_sender, "@") + 1); if ( SPF_request_set_helo_dom( spf_request, opt_helo ) ) { printf( "Invalid HELO domain.\n" ); res = 255; goto error; } } } } else { if ( SPF_request_set_helo_dom( spf_request, opt_helo ) ) { printf( "Invalid HELO domain.\n" ); res = 255; goto error; } } /* * record the envelope from email address from the SMTP MAIL FROM: * command. */ if ( SPF_request_set_env_from( spf_request, opt_sender ) ) { printf( "Invalid envelope from address.\n" ); res = 255; goto error; } /* * now that we have all the information, see what the result of * the SPF check is. */ SPF_request_query_mailfrom(spf_request, &spf_response); /* * If the sender MAIL FROM check failed, then for each SMTP RCPT TO * command, the mail might have come from a secondary MX for that * domain. * * Note that most MTAs will also check the RCPT TO command to make sure * that it is ok to accept. This SPF check won't give a free pass * to all secondary MXes from all domains, just the one specified by * the rcpt_to address. It is assumed that the MTA checks (at some * point) that we are also a valid primary or secondary for the domain. */ if (SPF_response_result(spf_response) != SPF_RESULT_PASS) { SPF_request_query_rcptto(spf_request, &spf_response_2mx, opt_rcpt_to); /* * We might now have a PASS if the mail came from a client which * is a secondary MX from the domain specified in opt_rcpt_to. * * If not, then the RCPT TO: address must have been a domain for * which the client is not a secondary MX, AND the MAIL FROM: domain * doesn't doesn't return 'pass' from SPF_result() */ if (SPF_response_result(spf_response_2mx) == SPF_RESULT_PASS) { } } /* * If the result is something like 'neutral', you probably * want to accept the email anyway, just like you would * when SPF_result() returns 'neutral'. * * It is possible that you will completely ignore the results * until the SMPT DATA command. */ if ( opt_debug > 0 ) { printf ( "result = %s (%d)\n", SPF_strresult(SPF_response_result(spf_response)), SPF_response_result(spf_response)); printf ( "err = %s (%d)\n", SPF_strerror(SPF_response_errcode(spf_response)), SPF_response_errcode(spf_response)); for (i = 0; i < SPF_response_messages(spf_response); i++) { SPF_error_t *err = SPF_response_message(spf_response, i); printf ( "%s_msg = (%d) %s\n", (SPF_error_errorp(err) ? "warn" : "err"), SPF_error_code(err), SPF_error_message(err)); } } #define VALID_STR(x) (x ? x : "") printf( "%s\n%s\n%s\n%s\n", SPF_strresult( SPF_response_result(spf_response) ), VALID_STR(SPF_response_get_smtp_comment(spf_response)), VALID_STR(SPF_response_get_header_comment(spf_response)), VALID_STR(SPF_response_get_received_spf(spf_response)) ); res = SPF_response_result(spf_response); /* * The response from the SPF check contains malloced data, so * make sure we free it. */ SPF_response_free(spf_response); if (spf_response_2mx) SPF_response_free(spf_response_2mx); error: /* * the SPF configuration variables contain malloced data, so we * have to vfree them also. */ if (spf_request) SPF_request_free(spf_request); if (spf_server) SPF_server_free(spf_server); return res; }