/*
* spf_example - An example program for how to use libspf2
*
* Author: Wayne Schlitt <wayne@midwestcs.com>
*
* 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 <stdio.h>
# include <stdlib.h> /* malloc / free */
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h> /* types (u_char .. etc..) */
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h> /* strstr / strdup */
#else
# ifdef HAVE_STRINGS_H
# include <strings.h> /* strstr / strdup */
# endif
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h> /* inet_ functions / structs */
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h> /* inet_ functions / structs */
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h> /* in_addr struct */
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h> /* DNS HEADER struct */
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#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 <IP address> The IP address that is sending email\n"
" -s <email address> The email address used as the\n"
" envelope-from. If no username (local\n"
" part) is given, 'postmaster' will be\n"
" assumed.\n"
" -r <email address> [optional] The email address used as\n"
" the envelope-to email address, for\n"
" secondary-MX checking.\n"
" -h <domain name> 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;
}
syntax highlighted by Code2HTML, v. 0.9.1