00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 #define SPF_TEST_VERSION "3.0"
00048
00049 #include "libreplace/win32_config.h"
00050
00051 #ifdef HAVE_CONFIG_H
00052 # include "config.h"
00053 #endif
00054
00055 #ifdef STDC_HEADERS
00056 # include <stdio.h>
00057 # include <stdlib.h>
00058 #endif
00059
00060 #ifdef HAVE_SYS_TYPES_H
00061 #include <sys/types.h>
00062 #endif
00063
00064 #ifdef HAVE_INTTYPES_H
00065 #include <inttypes.h>
00066 #endif
00067
00068 #ifdef HAVE_STRING_H
00069 # include <string.h>
00070 #else
00071 # ifdef HAVE_STRINGS_H
00072 # include <strings.h>
00073 # endif
00074 #endif
00075
00076 #ifdef HAVE_SYS_SOCKET_H
00077 # include <sys/socket.h>
00078 #endif
00079 #ifdef HAVE_NETINET_IN_H
00080 # include <netinet/in.h>
00081 #endif
00082
00083 #ifdef HAVE_ARPA_NAMESER_H
00084 # include <arpa/nameser.h>
00085 #endif
00086
00087 #ifdef HAVE_ARPA_INET_H
00088 # include <arpa/inet.h>
00089 #endif
00090
00091 #ifdef HAVE_GETOPT_LONG_ONLY
00092 #define _GNU_SOURCE
00093 #include <getopt.h>
00094 #else
00095 #include "libreplace/getopt.h"
00096 #endif
00097
00098 #ifdef _WIN32
00099 #include "spf_win32.h"
00100 #endif
00101
00102 #include "spf.h"
00103 #include "spf_dns.h"
00104 #include "spf_dns_null.h"
00105 #include "spf_dns_test.h"
00106 #include "spf_dns_cache.h"
00107 #ifndef _WIN32
00108 #include "spf_dns_resolv.h"
00109 #else
00110 #include "spf_dns_windns.h"
00111 #endif
00112
00113
00114
00115 #define TRUE 1
00116 #define FALSE 0
00117
00118 #define FREE(x, f) do { if ((x)) (f)((x)); (x) = NULL; } while(0)
00119 #define FREE_REQUEST(x) FREE((x), SPF_request_free)
00120 #define FREE_RESPONSE(x) FREE((x), SPF_response_free)
00121
00122 #define CONTINUE_ERROR do { res = 255; continue; } while(0)
00123 #define WARN_ERROR do { res = 255; } while(0)
00124 #define FAIL_ERROR do { res = 255; goto error; } while(0)
00125
00126 #define RESIZE_RESULT(n) do { \
00127 if (result == NULL) { \
00128 result_len = 256 + n; \
00129 result = malloc(result_len); \
00130 result[0] = '\0'; \
00131 } \
00132 else if (strlen(result) + n >= result_len) { \
00133 result_len = result_len + (result_len >> 1) + 8 + n; \
00134 result = realloc(result, result_len); \
00135 } \
00136 } while(0)
00137 #define APPEND_RESULT(n) do { \
00138 partial_result = SPF_strresult(n); \
00139 RESIZE_RESULT(strlen(partial_result)); \
00140 strcat(result, partial_result); \
00141 } while(0)
00142
00143 #define X_OR_EMPTY(x) ((x) ? (x) : "")
00144
00145 static struct option long_options[] = {
00146 {"file", 1, 0, 'f'},
00147
00148 {"ip", 1, 0, 'i'},
00149 {"sender", 1, 0, 's'},
00150 {"helo", 1, 0, 'h'},
00151 {"rcpt-to", 1, 0, 'r'},
00152
00153 {"debug", 2, 0, 'd'},
00154 {"local", 1, 0, 'l'},
00155 {"trusted", 1, 0, 't'},
00156 {"guess", 1, 0, 'g'},
00157 {"default-explanation", 1, 0, 'e'},
00158 {"max-lookup", 1, 0, 'm'},
00159 {"sanitize", 1, 0, 'c'},
00160 {"name", 1, 0, 'n'},
00161 {"override", 1, 0, 'a'},
00162 {"fallback", 1, 0, 'z'},
00163
00164 {"keep-comments", 0, 0, 'k'},
00165 {"version", 0, 0, 'v'},
00166 {"help", 0, 0, '?'},
00167
00168 {0, 0, 0, 0}
00169 };
00170
00171 static void
00172 unimplemented(const char flag)
00173 {
00174 struct option *opt;
00175 int i;
00176
00177 for (i = 0; (opt = &long_options[i])->name; i++) {
00178 if (flag == opt->val) {
00179 fprintf(stderr, "Unimplemented option: -%s or -%c\n",
00180 opt->name, flag);
00181 return;
00182 }
00183 }
00184
00185 fprintf(stderr, "Unimplemented option: -%c\n", flag);
00186 }
00187
00188
00189 static void
00190 usage()
00191 {
00192 fprintf(
00193 stderr,
00194 "Usage:\n"
00195 "\n"
00196 "spfquery [control options | data options] ...\n"
00197 "\n"
00198 "Use the -help option for more information\n"
00199 );
00200 }
00201
00202 static void
00203 help()
00204 {
00205 fprintf(
00206 stderr,
00207 "Usage:\n"
00208 "\n"
00209 "spfquery [control options | data options] ...\n"
00210 "\n"
00211 "Valid data options are:\n"
00212 " -file <filename> read spf data from a file. Use '-'\n"
00213 " to read from stdin.\n"
00214 "\n"
00215 " -ip <IP address> The IP address that is sending email\n"
00216 " -sender <email address> The email address used as the\n"
00217 " envelope-from. If no username (local\n"
00218 " part) is given, 'postmaster' will be\n"
00219 " assumed.\n"
00220 " -helo <domain name> The domain name given on the SMTP HELO\n"
00221 " command. This is only needed if the\n"
00222 " -sender option is not given.\n"
00223 " -rcpt-to <email addresses> A comma separated lists of email addresses\n"
00224 " that will have email from their secondary\n"
00225 " MXes automatically allowed.\n"
00226 "\n"
00227 "The data options are required. The -file option conflicts with all\n"
00228 "the other data options. The -helo and -rcpt-to are optional.\n"
00229 "\n"
00230 "\n"
00231 "Valid control options are:\n"
00232 " -debug [debug level] debug level.\n"
00233 " -local <SPF mechanisms> Local policy for whitelisting.\n"
00234 " -trusted <0|1> Should trusted-forwarder.org be checked?\n"
00235 " -guess <SPF mechanisms> Default checks if no SPF record is found.\n"
00236 " -default-explanation <str> Default explanation string to use.\n"
00237 " -max-lookup <number> Maximum number of DNS lookups to allow\n"
00238 " -sanitize <0|1> Clean up invalid characters in output?\n"
00239 " -name <domain name> The name of the system doing the SPF\n"
00240 " checking\n"
00241 " -override <...> Override SPF records for domains\n"
00242 " -fallback <...> Fallback SPF records for domains\n"
00243 "\n"
00244 " -keep-comments Print comments found when reading\n"
00245 " from a file.\n"
00246 " -version Print version of spfquery.\n"
00247 " -help Print out these options.\n"
00248 "\n"
00249 "Examples:\n"
00250 "\n"
00251 "spfquery -ip=11.22.33.44 -sender=user@aol.com -helo=spammer.tld\n"
00252 "spfquery -f test_data\n"
00253 "echo \"127.0.0.1 myname@mydomain.com helohost.com\" | spfquery -f -\n"
00254 );
00255 }
00256
00257
00258 static void
00259 response_print_errors(const char *context,
00260 SPF_response_t *spf_response, SPF_errcode_t err)
00261 {
00262 SPF_error_t *spf_error;
00263 int i;
00264
00265 printf("StartError\n");
00266
00267 if (context != NULL)
00268 printf("Context: %s\n", context);
00269 if (err != SPF_E_SUCCESS)
00270 printf("ErrorCode: (%d) %s\n", err, SPF_strerror(err));
00271
00272 if (spf_response != NULL) {
00273 for (i = 0; i < SPF_response_messages(spf_response); i++) {
00274 spf_error = SPF_response_message(spf_response, i);
00275 printf( "%s: %s%s\n",
00276 SPF_error_errorp(spf_error) ? "Error" : "Warning",
00277
00278
00279 ((SPF_error_errorp(spf_error) && (!err))
00280 ? "[UNRETURNED] "
00281 : ""),
00282 SPF_error_message(spf_error) );
00283 }
00284 }
00285 else {
00286 printf("libspf2 gave a NULL spf_response\n");
00287 }
00288 printf("EndError\n");
00289 }
00290
00291 static void
00292 response_print(const char *context, SPF_response_t *spf_response)
00293 {
00294 printf("--vv--\n");
00295 printf("Context: %s\n", context);
00296 if (spf_response == NULL) {
00297 printf("NULL RESPONSE!\n");
00298 }
00299 else {
00300 printf("Response result: %s\n",
00301 SPF_strresult(SPF_response_result(spf_response)));
00302 printf("Response reason: %s\n",
00303 SPF_strreason(SPF_response_reason(spf_response)));
00304 printf("Response err: %s\n",
00305 SPF_strerror(SPF_response_errcode(spf_response)));
00306 response_print_errors(NULL, spf_response,
00307 SPF_response_errcode(spf_response));
00308 }
00309 printf("--^^--\n");
00310 }
00311
00312 typedef
00313 struct SPF_client_options_struct {
00314
00315 char *localpolicy;
00316 const char *explanation;
00317 const char *fallback;
00318 const char *rec_dom;
00319 int use_trusted;
00320 int max_lookup;
00321 int sanitize;
00322 int debug;
00323 } SPF_client_options_t;
00324
00325 typedef
00326 struct SPF_client_request_struct {
00327 char *ip;
00328 char *sender;
00329 char *helo;
00330 char *rcpt_to;
00331 } SPF_client_request_t;
00332
00333 int main( int argc, char *argv[] )
00334 {
00335 SPF_client_options_t *opts;
00336 SPF_client_request_t *req;
00337
00338 SPF_server_t *spf_server = NULL;
00339 SPF_request_t *spf_request = NULL;
00340 SPF_response_t *spf_response = NULL;
00341 SPF_response_t *spf_response_2mx = NULL;
00342 SPF_response_t *spf_response_fallback = NULL;
00343 SPF_errcode_t err;
00344
00345 char *opt_file = NULL;
00346 int opt_keep_comments = 0;
00347
00348 FILE *fin;
00349 char in_line[4096];
00350 char *p, *p_end;
00351 int done_once;
00352 int major, minor, patch;
00353
00354 int res = 0;
00355 int c;
00356
00357 const char *partial_result;
00358 char *result = NULL;
00359 int result_len = 0;
00360
00361 opts = (SPF_client_options_t *)malloc(sizeof(SPF_client_options_t));
00362 memset(opts, 0, sizeof(SPF_client_options_t));
00363
00364 req = (SPF_client_request_t *)malloc(sizeof(SPF_client_request_t));
00365 memset(req, 0, sizeof(SPF_client_request_t));
00366
00367 opts->rec_dom = "spfquery";
00368
00369 #ifdef _WIN32
00370 if (SPF_win32_startup() == 0) {
00371 fprintf( stderr, "Could not startup WinSock, wrong version." );
00372 FAIL_ERROR;
00373 }
00374 #endif
00375
00376
00377
00378
00379
00380 for (;;) {
00381 int option_index;
00382
00383 c = getopt_long_only (argc, argv, "f:i:s:h:r:lt::gemcnd::kz:a:v",
00384 long_options, &option_index);
00385
00386 if (c == -1)
00387 break;
00388
00389 switch (c) {
00390 case 'f':
00391 opt_file = optarg;
00392 break;
00393
00394
00395 case 'i':
00396 req->ip = optarg;
00397 break;
00398
00399 case 's':
00400 req->sender = optarg;
00401 break;
00402
00403 case 'h':
00404 req->helo = optarg;
00405 break;
00406
00407 case 'r':
00408 req->rcpt_to = optarg;
00409 break;
00410
00411
00412 case 'l':
00413 opts->localpolicy = optarg;
00414 break;
00415
00416 case 't':
00417 if (optarg == NULL)
00418 opts->use_trusted = 1;
00419 else
00420 opts->use_trusted = atoi(optarg);
00421 break;
00422
00423 case 'g':
00424 opts->fallback = optarg;
00425 break;
00426
00427 case 'e':
00428 opts->explanation = optarg;
00429 break;
00430
00431 case 'm':
00432 opts->max_lookup = atoi(optarg);
00433 break;
00434
00435 case 'c':
00436 opts->sanitize = atoi(optarg);
00437 break;
00438
00439 case 'n':
00440 opts->rec_dom = optarg;
00441 break;
00442
00443 case 'a':
00444 unimplemented('a');
00445 break;
00446
00447 case 'z':
00448 unimplemented('z');
00449 break;
00450
00451
00452 case 'v':
00453 fprintf( stderr, "spfquery version information:\n" );
00454 fprintf( stderr, "SPF test system version: %s\n",
00455 SPF_TEST_VERSION );
00456 fprintf( stderr, "Compiled with SPF library version: %d.%d.%d\n",
00457 SPF_LIB_VERSION_MAJOR, SPF_LIB_VERSION_MINOR,
00458 SPF_LIB_VERSION_PATCH );
00459 SPF_get_lib_version( &major, &minor, &patch );
00460 fprintf( stderr, "Running with SPF library version: %d.%d.%d\n",
00461 major, minor, patch );
00462 fprintf( stderr, "\n" );
00463 usage();
00464 FAIL_ERROR;
00465 break;
00466
00467 case 0:
00468 case '?':
00469 help();
00470 FAIL_ERROR;
00471 break;
00472
00473 case 'k':
00474 opt_keep_comments = 1;
00475 break;
00476
00477 case 'd':
00478 if (optarg == NULL)
00479 opts->debug = 1;
00480 else
00481 opts->debug = atoi( optarg );
00482 break;
00483
00484 default:
00485 fprintf( stderr, "Error: getopt returned character code 0%o ??\n", c);
00486 FAIL_ERROR;
00487 }
00488 }
00489
00490 if (optind != argc) {
00491 help();
00492 FAIL_ERROR;
00493 }
00494
00495
00496
00497
00498
00499 spf_server = SPF_server_new(SPF_DNS_CACHE, opts->debug);
00500
00501 if ( opts->rec_dom )
00502 SPF_server_set_rec_dom( spf_server, opts->rec_dom );
00503 if ( opts->sanitize )
00504 SPF_server_set_sanitize( spf_server, opts->sanitize );
00505 if ( opts->max_lookup )
00506 SPF_server_set_max_dns_mech(spf_server, opts->max_lookup);
00507
00508 if (opts->localpolicy) {
00509 err = SPF_server_set_localpolicy( spf_server, opts->localpolicy, opts->use_trusted, &spf_response);
00510 if ( err ) {
00511 response_print_errors("Error setting local policy",
00512 spf_response, err);
00513 WARN_ERROR;
00514 }
00515 FREE_RESPONSE(spf_response);
00516 }
00517
00518
00519 if ( opts->explanation ) {
00520 err = SPF_server_set_explanation( spf_server, opts->explanation, &spf_response );
00521 if ( err ) {
00522 response_print_errors("Error setting default explanation",
00523 spf_response, err);
00524 WARN_ERROR;
00525 }
00526 FREE_RESPONSE(spf_response);
00527 }
00528
00529
00530
00531
00532
00533 if (opt_file) {
00534
00535
00536
00537 if (strcmp(opt_file, "-" ) == 0)
00538 fin = stdin;
00539 else
00540 fin = fopen( opt_file, "r" );
00541
00542 if (!fin) {
00543 fprintf( stderr, "Could not open: %s\n", opt_file );
00544 FAIL_ERROR;
00545 }
00546 }
00547 else {
00548 fin = NULL;
00549
00550 if ((req->ip == NULL) ||
00551 (req->sender == NULL && req->helo == NULL) ) {
00552 usage();
00553 FAIL_ERROR;
00554 }
00555 }
00556
00557 done_once = FALSE;
00558
00559 while ( TRUE ) {
00560 if ( fin ) {
00561 if ( fgets( in_line, sizeof( in_line ), fin ) == NULL )
00562 break;
00563
00564 in_line[strcspn(in_line, "\r\n")] = '\0';
00565 p = in_line;
00566
00567 p += strspn( p, " \t\n" );
00568 {
00569 if ( *p == '\0' || *p == '#' ) {
00570 if ( opt_keep_comments )
00571 printf( "%s\n", in_line );
00572 continue;
00573 }
00574 }
00575 req->ip = p;
00576 p += strcspn( p, " \t\n" );
00577 *p++ = '\0';
00578
00579 p += strspn( p, " \t\n" );
00580 req->sender = p;
00581 p += strcspn( p, " \t\n" );
00582 *p++ = '\0';
00583
00584 p += strspn( p, " \t\n" );
00585 req->helo = p;
00586 p += strcspn( p, " \t\n" );
00587 *p++ = '\0';
00588
00589 p += strspn( p, " \t\n" );
00590 req->rcpt_to = p;
00591 p += strcspn( p, " \t\n" );
00592 *p++ = '\0';
00593 }
00594 else {
00595 if ( done_once )
00596 break;
00597 done_once = TRUE;
00598 }
00599
00600
00601 FREE_REQUEST(spf_request);
00602 FREE_RESPONSE(spf_response);
00603
00604 spf_request = SPF_request_new(spf_server);
00605
00606 if (SPF_request_set_ipv4_str(spf_request, req->ip)
00607 && SPF_request_set_ipv6_str(spf_request, req->ip)) {
00608 printf( "Invalid IP address.\n" );
00609 CONTINUE_ERROR;
00610 }
00611
00612 if (req->helo) {
00613 if (SPF_request_set_helo_dom( spf_request, req->helo ) ) {
00614 printf( "Invalid HELO domain.\n" );
00615 CONTINUE_ERROR;
00616 }
00617 }
00618
00619 if (SPF_request_set_env_from( spf_request, req->sender ) ) {
00620 printf( "Invalid envelope from address.\n" );
00621 CONTINUE_ERROR;
00622 }
00623
00624 err = SPF_request_query_mailfrom(spf_request, &spf_response);
00625 if (opts->debug)
00626 response_print("Main query", spf_response);
00627 if (err) {
00628 response_print_errors("Failed to query MAIL-FROM",
00629 spf_response, err);
00630 CONTINUE_ERROR;
00631 }
00632
00633 if (result != NULL)
00634 result[0] = '\0';
00635 APPEND_RESULT(SPF_response_result(spf_response));
00636
00637 if (req->rcpt_to != NULL && *req->rcpt_to != '\0' ) {
00638 p = req->rcpt_to;
00639 p_end = p + strcspn(p, ",;");
00640
00641
00642 while (SPF_response_result(spf_response)!=SPF_RESULT_PASS) {
00643 if (*p_end)
00644 *p_end = '\0';
00645 else
00646 p_end = NULL;
00647
00648 err = SPF_request_query_rcptto(spf_request,
00649 &spf_response_2mx, p);
00650 if (opts->debug)
00651 response_print("2mx query", spf_response_2mx);
00652 if (err) {
00653 response_print_errors("Failed to query RCPT-TO",
00654 spf_response, err);
00655 CONTINUE_ERROR;
00656 }
00657
00658
00659 APPEND_RESULT(SPF_response_result(spf_response_2mx));
00660
00661 spf_response = SPF_response_combine(spf_response,
00662 spf_response_2mx);
00663
00664 if (!p_end)
00665 break;
00666 p = p_end + 1;
00667 }
00668 }
00669
00670
00671 if (opts->fallback) {
00672 err = SPF_request_query_fallback(spf_request,
00673 &spf_response_fallback, opts->fallback);
00674 if (opts->debug)
00675 response_print("fallback query", spf_response_fallback);
00676 if (err) {
00677 response_print_errors("Failed to query best-guess",
00678 spf_response_fallback, err);
00679 CONTINUE_ERROR;
00680 }
00681
00682
00683 APPEND_RESULT(SPF_response_result(spf_response_fallback));
00684
00685 spf_response = SPF_response_combine(spf_response,
00686 spf_response_fallback);
00687 }
00688
00689 printf( "%s\n%s\n%s\n%s\n",
00690 result,
00691 X_OR_EMPTY(SPF_response_get_smtp_comment(spf_response)),
00692 X_OR_EMPTY(SPF_response_get_header_comment(spf_response)),
00693 X_OR_EMPTY(SPF_response_get_received_spf(spf_response))
00694 );
00695
00696 res = SPF_response_result(spf_response);
00697
00698 fflush(stdout);
00699 }
00700
00701 error:
00702 FREE(result, free);
00703 FREE_RESPONSE(spf_response);
00704 FREE_REQUEST(spf_request);
00705 FREE(spf_server, SPF_server_free);
00706
00707 FREE(req, free);
00708 FREE(opts, free);
00709
00710 #ifdef _WIN32
00711 SPF_win32_cleanup();
00712 #endif
00713
00714 return res;
00715 }