libspf2 1.2.10
spfquery.c
Go to the documentation of this file.
00001 /*
00002  *  spfquery - Sender Policy Framwork command line utility
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 free software; you can redistribute it and/or modify
00011  * it under the terms of either:
00012  *
00013  *   a) The GNU Lesser General Public License as published by the Free
00014  *        Software Foundation; either version 2.1, or (at your option) any
00015  *        later version,
00016  *
00017  *   OR
00018  *
00019  *   b) The two-clause BSD license.
00020  *
00021  *
00022  * The two-clause BSD license:
00023  *
00024  *
00025  * Redistribution and use in source and binary forms, with or without
00026  * modification, are permitted provided that the following conditions
00027  * are met:
00028  *
00029  * 1. Redistributions of source code must retain the above copyright
00030  *      notice, this list of conditions and the following disclaimer.
00031  * 2. Redistributions in binary form must reproduce the above copyright
00032  *      notice, this list of conditions and the following disclaimer in the
00033  *      documentation and/or other materials provided with the distribution.
00034  *
00035  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00036  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00037  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00038  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00039  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00040  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00041  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00042  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00043  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00044  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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>       /* malloc / free */
00056 #endif
00057 
00058 #ifdef HAVE_SYS_TYPES_H
00059 #include <sys/types.h>  /* types (u_char .. etc..) */
00060 #endif
00061 
00062 #ifdef HAVE_INTTYPES_H
00063 #include <inttypes.h>
00064 #endif
00065 
00066 #ifdef HAVE_STRING_H
00067 # include <string.h>       /* strstr / strdup */
00068 #else
00069 # ifdef HAVE_STRINGS_H
00070 #  include <strings.h>     /* strstr / strdup */
00071 # endif
00072 #endif
00073 
00074 #ifdef HAVE_SYS_SOCKET_H
00075 # include <sys/socket.h>   /* inet_ functions / structs */
00076 #endif
00077 #ifdef HAVE_NETINET_IN_H
00078 # include <netinet/in.h>   /* inet_ functions / structs */
00079 #endif
00080 
00081 #ifdef HAVE_ARPA_NAMESER_H
00082 # include <arpa/nameser.h> /* DNS HEADER struct */
00083 #endif
00084 
00085 #ifdef HAVE_ARPA_INET_H
00086 # include <arpa/inet.h> /* in_addr struct */
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                                         // SPF_error_code(spf_error),
00272                                         // SPF_strerror(SPF_error_code(spf_error)),
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         // void         *hook;
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          * check the arguments
00372          */
00373 
00374         for (;;) {
00375                 int option_index;       /* Largely unused */
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':               /* "clean" */
00430                                 opts->sanitize = atoi(optarg);
00431                                 break;
00432 
00433                         case 'n':               /* name of host doing SPF checking */
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          * set up the SPF configuration
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          * process the SPF request
00525          */
00526 
00527         if (opt_file) {
00528                 /*
00529                  * the requests are on STDIN
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                 /* We have to do this here else we leak on CONTINUE_ERROR */
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                         /* This is some incarnation of 2mx mode. */
00636                         while (SPF_response_result(spf_response)!=SPF_RESULT_PASS) {
00637                                 if (*p_end)
00638                                         *p_end = '\0';
00639                                 else
00640                                         p_end = NULL;   /* Note this is last rcpt */
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                                 /* append the result */
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                 /* We now have an option to call SPF_request_query_fallback */
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                         /* append the result */
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 }