libspf2 1.2.10
|
00001 /* 00002 * spf_example - An example program for how to use libspf2 00003 * 00004 * Author: Wayne Schlitt <wayne@midwestcs.com> 00005 * 00006 * File: spfquery.c 00007 * Desc: SPF command line utility 00008 * 00009 * 00010 * This program is in the public domain, there is no copyright, you 00011 * can do anything you want with it. 00012 */ 00013 00014 00015 /* 00016 * The libspf2 library uses the GNU autoconf system to help make 00017 * the library more portable. The config.h file should have the 00018 * HAVE_xxx defines that are appropriate for your system. Either use 00019 * autconf to create it, or create it by hand. 00020 */ 00021 00022 00023 #ifdef HAVE_CONFIG_H 00024 # include "config.h" 00025 #endif 00026 00027 #ifdef STDC_HEADERS 00028 # include <stdio.h> 00029 # include <stdlib.h> /* malloc / free */ 00030 #endif 00031 00032 #ifdef HAVE_SYS_TYPES_H 00033 #include <sys/types.h> /* types (u_char .. etc..) */ 00034 #endif 00035 00036 #ifdef HAVE_INTTYPES_H 00037 #include <inttypes.h> 00038 #endif 00039 00040 #ifdef HAVE_STRING_H 00041 # include <string.h> /* strstr / strdup */ 00042 #else 00043 # ifdef HAVE_STRINGS_H 00044 # include <strings.h> /* strstr / strdup */ 00045 # endif 00046 #endif 00047 00048 #ifdef HAVE_SYS_SOCKET_H 00049 # include <sys/socket.h> /* inet_ functions / structs */ 00050 #endif 00051 #ifdef HAVE_NETINET_IN_H 00052 # include <netinet/in.h> /* inet_ functions / structs */ 00053 #endif 00054 #ifdef HAVE_ARPA_INET_H 00055 # include <arpa/inet.h> /* in_addr struct */ 00056 #endif 00057 00058 #ifdef HAVE_ARPA_NAMESER_H 00059 # include <arpa/nameser.h> /* DNS HEADER struct */ 00060 #endif 00061 00062 #ifdef HAVE_UNISTD_H 00063 #include <unistd.h> 00064 #endif 00065 00066 #ifdef HAVE_GETOPT_H 00067 #include <getopt.h> 00068 #endif 00069 00070 00071 00072 /* 00073 * libspf2 public include files that are needed for this example 00074 * program 00075 */ 00076 00077 #include "spf.h" 00078 00079 00080 /* 00081 * usage() just prints out the command line options for this program 00082 */ 00083 static void usage() 00084 { 00085 fprintf( 00086 stderr, 00087 "Usage:\n" 00088 "\n" 00089 "spf_example [options]\n" 00090 "\n" 00091 "Valid data options are:\n" 00092 " -i <IP address> The IP address that is sending email\n" 00093 " -s <email address> The email address used as the\n" 00094 " envelope-from. If no username (local\n" 00095 " part) is given, 'postmaster' will be\n" 00096 " assumed.\n" 00097 " -r <email address> [optional] The email address used as\n" 00098 " the envelope-to email address, for\n" 00099 " secondary-MX checking.\n" 00100 " -h <domain name> The domain name given on the SMTP HELO\n" 00101 " command. This is only needed if the\n" 00102 " -sender option is not given.\n" 00103 " -d [debug level] debug level.\n" 00104 ); 00105 } 00106 00107 00108 00109 /* 00110 * All the code is in the main routine, but most usages of libspf2 00111 * would have the code spread around into various subrotines. 00112 */ 00113 00114 int main( int argc, char *argv[] ) 00115 { 00116 int c; 00117 int res = 0; 00118 int i; 00119 00120 char *opt_ip = NULL; 00121 char *opt_sender = NULL; 00122 char *opt_helo = NULL; 00123 char *opt_rcpt_to = NULL; 00124 int opt_debug = 0; 00125 00126 /* You should not indirect on any of these structures, as their 00127 * layout may change between versions of the library. Use the 00128 * accessor functions instead. Definitions of the structs may not 00129 * even be provided. */ 00130 00131 SPF_server_t *spf_server = NULL; 00132 SPF_request_t *spf_request = NULL; 00133 SPF_response_t *spf_response = NULL; 00134 SPF_response_t *spf_response_2mx = NULL; 00135 00136 00137 /* 00138 * check the arguments 00139 */ 00140 00141 while (1) 00142 { 00143 c = getopt(argc, argv, "i:s:h:r:d::" ); 00144 00145 if (c == -1) 00146 break; 00147 00148 switch (c) 00149 { 00150 case 'i': 00151 opt_ip = optarg; 00152 break; 00153 00154 case 's': 00155 opt_sender = optarg; 00156 break; 00157 00158 case 'h': 00159 opt_helo = optarg; 00160 break; 00161 00162 case 'r': 00163 opt_rcpt_to = optarg; 00164 break; 00165 00166 case 0: 00167 case '?': 00168 usage(); 00169 res = 255; 00170 goto error; 00171 break; 00172 00173 case 'd': 00174 if (optarg == NULL) 00175 opt_debug = 1; 00176 else 00177 opt_debug = atoi( optarg ); 00178 break; 00179 00180 default: 00181 fprintf( stderr, "Error: getopt returned character code 0%o ??\n", c); 00182 } 00183 } 00184 00185 if (optind != argc 00186 || opt_ip == NULL 00187 || (opt_helo == NULL && opt_sender == NULL)) 00188 { 00189 usage(); 00190 res = 255; 00191 goto error; 00192 } 00193 00194 /* 00195 * Configure the SPF system. 00196 * 00197 * libspf2 is designed so that configurations can be set up once 00198 * and reused many times different emails delivered in a single SMTP 00199 * session or in different SMTP sessions. 00200 */ 00201 00202 /* 00203 * set up the SPF server 00204 * 00205 * Configurations contain malloc'd data so must be 00206 * destroyed when you are finished. 00207 */ 00208 00209 spf_server = SPF_server_new(SPF_DNS_CACHE, 1); 00210 00211 if (spf_server == NULL) { 00212 fprintf( stderr, "SPF_create_config failed.\n" ); 00213 res = 255; 00214 goto error; 00215 } 00216 00217 /* 00218 * Create a new request. 00219 * 00220 * The SPF request contains all the data needed to process 00221 * the SPF check. Requests are malloc'd so it must be 00222 * destroyed when you are finished with it. 00223 */ 00224 00225 spf_request = SPF_request_new(spf_server); 00226 00227 /* The domain name of the receiving MTA will default to gethostname() */ 00228 /* SPF_request_set_rec_dom( spf_request, opt_name ); */ 00229 00230 00231 /* 00232 * process the SPF request 00233 * 00234 * Now that the SPF system has been configured, we can process the requests. 00235 * There would normally be a loop around this code or it would be placed 00236 * in a subroutine to be called for each email. 00237 * 00238 * If a single email session sends several emails, you don't need to 00239 * reset the IP address or the HELO domain each time, just change the 00240 * envelope from. 00241 */ 00242 00243 /* 00244 * record the IP address of the client (sending) MTA. 00245 * 00246 * There are other SPF_set_ip*() functionx if you have a structure 00247 * instead of a string. 00248 */ 00249 00250 if ( SPF_request_set_ipv4_str( spf_request, opt_ip ) ) { 00251 printf( "Invalid IP address.\n" ); 00252 res = 255; 00253 goto error; 00254 } 00255 00256 00257 /* 00258 * record the HELO domain name of the client (sending) MTA from 00259 * the SMTP HELO or EHLO commands 00260 * 00261 * This domain name will be used if the envelope from address is 00262 * null (e.g. MAIL FROM:<>). This happens when a bounce is being 00263 * sent and, in effect, it is the client MTA that is sending the 00264 * message. 00265 */ 00266 00267 if ( SPF_request_set_helo_dom( spf_request, opt_helo ) ) { 00268 printf( "Invalid HELO domain.\n" ); 00269 res = 255; 00270 goto error; 00271 } 00272 00273 /* 00274 * record the envelope from email address from the SMTP MAIL FROM: 00275 * command. 00276 */ 00277 00278 if ( SPF_request_set_env_from( spf_request, opt_sender ) ) { 00279 printf( "Invalid envelope from address.\n" ); 00280 res = 255; 00281 goto error; 00282 } 00283 00284 /* 00285 * now that we have all the information, see what the result of 00286 * the SPF check is. 00287 */ 00288 00289 SPF_request_query_mailfrom(spf_request, &spf_response); 00290 00291 /* 00292 * If the sender MAIL FROM check failed, then for each SMTP RCPT TO 00293 * command, the mail might have come from a secondary MX for that 00294 * domain. 00295 * 00296 * Note that most MTAs will also check the RCPT TO command to make sure 00297 * that it is ok to accept. This SPF check won't give a free pass 00298 * to all secondary MXes from all domains, just the one specified by 00299 * the rcpt_to address. It is assumed that the MTA checks (at some 00300 * point) that we are also a valid primary or secondary for the domain. 00301 */ 00302 if (SPF_response_result(spf_response) != SPF_RESULT_PASS) { 00303 SPF_request_query_rcptto(spf_request, &spf_response_2mx, opt_rcpt_to); 00304 /* 00305 * We might now have a PASS if the mail came from a client which 00306 * is a secondary MX from the domain specified in opt_rcpt_to. 00307 * 00308 * If not, then the RCPT TO: address must have been a domain for 00309 * which the client is not a secondary MX, AND the MAIL FROM: domain 00310 * doesn't doesn't return 'pass' from SPF_result() 00311 */ 00312 if (SPF_response_result(spf_response_2mx) == SPF_RESULT_PASS) { 00313 } 00314 } 00315 00316 /* 00317 * If the result is something like 'neutral', you probably 00318 * want to accept the email anyway, just like you would 00319 * when SPF_result() returns 'neutral'. 00320 * 00321 * It is possible that you will completely ignore the results 00322 * until the SMPT DATA command. 00323 */ 00324 00325 if ( opt_debug > 0 ) { 00326 printf ( "result = %s (%d)\n", 00327 SPF_strresult(SPF_response_result(spf_response)), 00328 SPF_response_result(spf_response)); 00329 printf ( "err = %s (%d)\n", 00330 SPF_strerror(SPF_response_errcode(spf_response)), 00331 SPF_response_errcode(spf_response)); 00332 for (i = 0; i < SPF_response_messages(spf_response); i++) { 00333 SPF_error_t *err = SPF_response_message(spf_response, i); 00334 printf ( "%s_msg = (%d) %s\n", 00335 (SPF_error_errorp(err) ? "warn" : "err"), 00336 SPF_error_code(err), 00337 SPF_error_message(err)); 00338 } 00339 } 00340 00341 #define VALID_STR(x) (x ? x : "") 00342 00343 printf( "%s\n%s\n%s\n%s\n", 00344 SPF_strresult( SPF_response_result(spf_response) ), 00345 VALID_STR(SPF_response_get_smtp_comment(spf_response)), 00346 VALID_STR(SPF_response_get_header_comment(spf_response)), 00347 VALID_STR(SPF_response_get_received_spf(spf_response)) 00348 ); 00349 00350 res = SPF_response_result(spf_response); 00351 00352 00353 /* 00354 * The response from the SPF check contains malloced data, so 00355 * make sure we free it. 00356 */ 00357 00358 SPF_response_free(spf_response); 00359 if (spf_response_2mx) 00360 SPF_response_free(spf_response_2mx); 00361 00362 error: 00363 00364 /* 00365 * the SPF configuration variables contain malloced data, so we 00366 * have to vfree them also. 00367 */ 00368 00369 if (spf_request) 00370 SPF_request_free(spf_request); 00371 if (spf_server) 00372 SPF_server_free(spf_server); 00373 return res; 00374 }