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