libspf2 1.2.11
spfquery.c
Go to the documentation of this file.
1/*
2 * spfquery - Sender Policy Framwork command line utility
3 *
4 * Author: Wayne Schlitt <wayne@midwestcs.com>
5 *
6 * File: spfquery.c
7 * Desc: SPF command line utility
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of either:
12 *
13 * a) The GNU Lesser General Public License as published by the Free
14 * Software Foundation; either version 2.1, or (at your option) any
15 * later version,
16 *
17 * OR
18 *
19 * b) The two-clause BSD license.
20 *
21 *
22 * The two-clause BSD license:
23 *
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 *
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
36 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
38 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
39 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
40 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
41 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
42 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
43 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
44 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 */
46
47#define SPF_TEST_VERSION "3.0"
48
50
51#ifdef HAVE_CONFIG_H
52# include "config.h"
53#endif
54
55#ifdef STDC_HEADERS
56# include <stdio.h>
57# include <stdlib.h> /* malloc / free */
58#endif
59
60#ifdef HAVE_SYS_TYPES_H
61#include <sys/types.h> /* types (u_char .. etc..) */
62#endif
63
64#ifdef HAVE_INTTYPES_H
65#include <inttypes.h>
66#endif
67
68#ifdef HAVE_STRING_H
69# include <string.h> /* strstr / strdup */
70#else
71# ifdef HAVE_STRINGS_H
72# include <strings.h> /* strstr / strdup */
73# endif
74#endif
75
76#ifdef HAVE_SYS_SOCKET_H
77# include <sys/socket.h> /* inet_ functions / structs */
78#endif
79#ifdef HAVE_NETINET_IN_H
80# include <netinet/in.h> /* inet_ functions / structs */
81#endif
82
83#ifdef HAVE_ARPA_NAMESER_H
84# include <arpa/nameser.h> /* DNS HEADER struct */
85#endif
86
87#ifdef HAVE_ARPA_INET_H
88# include <arpa/inet.h> /* in_addr struct */
89#endif
90
91#ifdef HAVE_GETOPT_LONG_ONLY
92#define _GNU_SOURCE
93#include <getopt.h>
94#else
95#include "libreplace/getopt.h"
96#endif
97
98#ifdef _WIN32
99#include "spf_win32.h"
100#endif
101
102#include "spf.h"
103#include "spf_dns.h"
104#include "spf_dns_null.h"
105#include "spf_dns_test.h"
106#include "spf_dns_cache.h"
107#ifndef _WIN32
108#include "spf_dns_resolv.h"
109#else
110#include "spf_dns_windns.h"
111#endif
112
113
114
115#define TRUE 1
116#define FALSE 0
117
118#define FREE(x, f) do { if ((x)) (f)((x)); (x) = NULL; } while(0)
119#define FREE_REQUEST(x) FREE((x), SPF_request_free)
120#define FREE_RESPONSE(x) FREE((x), SPF_response_free)
121
122#define CONTINUE_ERROR do { res = 255; continue; } while(0)
123#define WARN_ERROR do { res = 255; } while(0)
124#define FAIL_ERROR do { res = 255; goto error; } while(0)
125
126#define RESIZE_RESULT(n) do { \
127 if (result == NULL) { \
128 result_len = 256 + n; \
129 result = malloc(result_len); \
130 result[0] = '\0'; \
131 } \
132 else if (strlen(result) + n >= result_len) { \
133 result_len = result_len + (result_len >> 1) + 8 + n; \
134 result = realloc(result, result_len); \
135 } \
136} while(0)
137#define APPEND_RESULT(n) do { \
138 partial_result = SPF_strresult(n); \
139 RESIZE_RESULT(strlen(partial_result)); \
140 strcat(result, partial_result); \
141} while(0)
142
143#define X_OR_EMPTY(x) ((x) ? (x) : "")
144
145static struct option long_options[] = {
146 {"file", 1, 0, 'f'},
147
148 {"ip", 1, 0, 'i'},
149 {"sender", 1, 0, 's'},
150 {"helo", 1, 0, 'h'},
151 {"rcpt-to", 1, 0, 'r'},
152
153 {"debug", 2, 0, 'd'},
154 {"local", 1, 0, 'l'},
155 {"trusted", 1, 0, 't'},
156 {"guess", 1, 0, 'g'},
157 {"default-explanation", 1, 0, 'e'},
158 {"max-lookup", 1, 0, 'm'},
159 {"sanitize", 1, 0, 'c'},
160 {"name", 1, 0, 'n'},
161 {"override", 1, 0, 'a'},
162 {"fallback", 1, 0, 'z'},
163
164 {"keep-comments", 0, 0, 'k'},
165 {"version", 0, 0, 'v'},
166 {"help", 0, 0, '?'},
167
168 {0, 0, 0, 0}
169};
170
171static void
172unimplemented(const char flag)
173{
174 struct option *opt;
175 int i;
176
177 for (i = 0; (opt = &long_options[i])->name; i++) {
178 if (flag == opt->val) {
179 fprintf(stderr, "Unimplemented option: -%s or -%c\n",
180 opt->name, flag);
181 return;
182 }
183 }
184
185 fprintf(stderr, "Unimplemented option: -%c\n", flag);
186}
187
188
189static void
190usage()
191{
192 fprintf(
193 stderr,
194 "Usage:\n"
195 "\n"
196 "spfquery [control options | data options] ...\n"
197 "\n"
198 "Use the -help option for more information\n"
199 );
200}
201
202static void
203help()
204{
205 fprintf(
206 stderr,
207 "Usage:\n"
208 "\n"
209 "spfquery [control options | data options] ...\n"
210 "\n"
211 "Valid data options are:\n"
212 " -file <filename> read spf data from a file. Use '-'\n"
213 " to read from stdin.\n"
214 "\n"
215 " -ip <IP address> The IP address that is sending email\n"
216 " -sender <email address> The email address used as the\n"
217 " envelope-from. If no username (local\n"
218 " part) is given, 'postmaster' will be\n"
219 " assumed.\n"
220 " -helo <domain name> The domain name given on the SMTP HELO\n"
221 " command. This is only needed if the\n"
222 " -sender option is not given.\n"
223 " -rcpt-to <email addresses> A comma separated lists of email addresses\n"
224 " that will have email from their secondary\n"
225 " MXes automatically allowed.\n"
226 "\n"
227 "The data options are required. The -file option conflicts with all\n"
228 "the other data options. The -helo and -rcpt-to are optional.\n"
229 "\n"
230 "\n"
231 "Valid control options are:\n"
232 " -debug [debug level] debug level.\n"
233 " -local <SPF mechanisms> Local policy for whitelisting.\n"
234 " -trusted <0|1> Should trusted-forwarder.org be checked?\n"
235 " -guess <SPF mechanisms> Default checks if no SPF record is found.\n"
236 " -default-explanation <str> Default explanation string to use.\n"
237 " -max-lookup <number> Maximum number of DNS lookups to allow\n"
238 " -sanitize <0|1> Clean up invalid characters in output?\n"
239 " -name <domain name> The name of the system doing the SPF\n"
240 " checking\n"
241 " -override <...> Override SPF records for domains\n"
242 " -fallback <...> Fallback SPF records for domains\n"
243 "\n"
244 " -keep-comments Print comments found when reading\n"
245 " from a file.\n"
246 " -version Print version of spfquery.\n"
247 " -help Print out these options.\n"
248 "\n"
249 "Examples:\n"
250 "\n"
251 "spfquery -ip=11.22.33.44 -sender=user@aol.com -helo=spammer.tld\n"
252 "spfquery -f test_data\n"
253 "echo \"127.0.0.1 myname@mydomain.com helohost.com\" | spfquery -f -\n"
254 );
255}
256
257
258static void
259response_print_errors(const char *context,
260 SPF_response_t *spf_response, SPF_errcode_t err)
261{
262 SPF_error_t *spf_error;
263 int i;
264
265 printf("StartError\n");
266
267 if (context != NULL)
268 printf("Context: %s\n", context);
269 if (err != SPF_E_SUCCESS)
270 printf("ErrorCode: (%d) %s\n", err, SPF_strerror(err));
271
272 if (spf_response != NULL) {
273 for (i = 0; i < SPF_response_messages(spf_response); i++) {
274 spf_error = SPF_response_message(spf_response, i);
275 printf( "%s: %s%s\n",
276 SPF_error_errorp(spf_error) ? "Error" : "Warning",
277 // SPF_error_code(spf_error),
278 // SPF_strerror(SPF_error_code(spf_error)),
279 ((SPF_error_errorp(spf_error) && (!err))
280 ? "[UNRETURNED] "
281 : ""),
282 SPF_error_message(spf_error) );
283 }
284 }
285 else {
286 printf("libspf2 gave a NULL spf_response\n");
287 }
288 printf("EndError\n");
289}
290
291static void
292response_print(const char *context, SPF_response_t *spf_response)
293{
294 printf("--vv--\n");
295 printf("Context: %s\n", context);
296 if (spf_response == NULL) {
297 printf("NULL RESPONSE!\n");
298 }
299 else {
300 printf("Response result: %s\n",
301 SPF_strresult(SPF_response_result(spf_response)));
302 printf("Response reason: %s\n",
303 SPF_strreason(SPF_response_reason(spf_response)));
304 printf("Response err: %s\n",
305 SPF_strerror(SPF_response_errcode(spf_response)));
306 response_print_errors(NULL, spf_response,
307 SPF_response_errcode(spf_response));
308 }
309 printf("--^^--\n");
310}
311
312typedef
313struct SPF_client_options_struct {
314 // void *hook;
316 const char *explanation;
317 const char *fallback;
318 const char *rec_dom;
322 int debug;
324
325typedef
326struct SPF_client_request_struct {
327 char *ip;
328 char *sender;
329 char *helo;
330 char *rcpt_to;
332
333int main( int argc, char *argv[] )
334{
337
338 SPF_server_t *spf_server = NULL;
339 SPF_request_t *spf_request = NULL;
340 SPF_response_t *spf_response = NULL;
341 SPF_response_t *spf_response_2mx = NULL;
342 SPF_response_t *spf_response_fallback = NULL;
343 SPF_errcode_t err;
344
345 char *opt_file = NULL;
346 int opt_keep_comments = 0;
347
348 FILE *fin;
349 char in_line[4096];
350 char *p, *p_end;
351 int done_once;
352 int major, minor, patch;
353
354 int res = 0;
355 int c;
356
357 const char *partial_result;
358 char *result = NULL;
359 int result_len = 0;
360
361 opts = (SPF_client_options_t *)malloc(sizeof(SPF_client_options_t));
362 memset(opts, 0, sizeof(SPF_client_options_t));
363
364 req = (SPF_client_request_t *)malloc(sizeof(SPF_client_request_t));
365 memset(req, 0, sizeof(SPF_client_request_t));
366
367 opts->rec_dom = "spfquery";
368
369#ifdef _WIN32
370 if (SPF_win32_startup() == 0) {
371 fprintf( stderr, "Could not startup WinSock, wrong version." );
373 }
374#endif
375
376 /*
377 * check the arguments
378 */
379
380 for (;;) {
381 int option_index; /* Largely unused */
382
383 c = getopt_long_only (argc, argv, "f:i:s:h:r:lt::gemcnd::kz:a:v",
384 long_options, &option_index);
385
386 if (c == -1)
387 break;
388
389 switch (c) {
390 case 'f':
391 opt_file = optarg;
392 break;
393
394
395 case 'i':
396 req->ip = optarg;
397 break;
398
399 case 's':
400 req->sender = optarg;
401 break;
402
403 case 'h':
404 req->helo = optarg;
405 break;
406
407 case 'r':
408 req->rcpt_to = optarg;
409 break;
410
411
412 case 'l':
413 opts->localpolicy = optarg;
414 break;
415
416 case 't':
417 if (optarg == NULL)
418 opts->use_trusted = 1;
419 else
420 opts->use_trusted = atoi(optarg);
421 break;
422
423 case 'g':
424 opts->fallback = optarg;
425 break;
426
427 case 'e':
428 opts->explanation = optarg;
429 break;
430
431 case 'm':
432 opts->max_lookup = atoi(optarg);
433 break;
434
435 case 'c': /* "clean" */
436 opts->sanitize = atoi(optarg);
437 break;
438
439 case 'n': /* name of host doing SPF checking */
440 opts->rec_dom = optarg;
441 break;
442
443 case 'a':
444 unimplemented('a');
445 break;
446
447 case 'z':
448 unimplemented('z');
449 break;
450
451
452 case 'v':
453 fprintf( stderr, "spfquery version information:\n" );
454 fprintf( stderr, "SPF test system version: %s\n",
456 fprintf( stderr, "Compiled with SPF library version: %d.%d.%d\n",
459 SPF_get_lib_version( &major, &minor, &patch );
460 fprintf( stderr, "Running with SPF library version: %d.%d.%d\n",
461 major, minor, patch );
462 fprintf( stderr, "\n" );
463 usage();
465 break;
466
467 case 0:
468 case '?':
469 help();
471 break;
472
473 case 'k':
474 opt_keep_comments = 1;
475 break;
476
477 case 'd':
478 if (optarg == NULL)
479 opts->debug = 1;
480 else
481 opts->debug = atoi( optarg );
482 break;
483
484 default:
485 fprintf( stderr, "Error: getopt returned character code 0%o ??\n", c);
487 }
488 }
489
490 if (optind != argc) {
491 help();
493 }
494
495 /*
496 * set up the SPF configuration
497 */
498
499 spf_server = SPF_server_new(SPF_DNS_CACHE, opts->debug);
500
501 if ( opts->rec_dom )
502 SPF_server_set_rec_dom( spf_server, opts->rec_dom );
503 if ( opts->sanitize )
504 SPF_server_set_sanitize( spf_server, opts->sanitize );
505 if ( opts->max_lookup )
506 SPF_server_set_max_dns_mech(spf_server, opts->max_lookup);
507
508 if (opts->localpolicy) {
509 err = SPF_server_set_localpolicy( spf_server, opts->localpolicy, opts->use_trusted, &spf_response);
510 if ( err ) {
511 response_print_errors("Error setting local policy",
512 spf_response, err);
514 }
515 FREE_RESPONSE(spf_response);
516 }
517
518
519 if ( opts->explanation ) {
520 err = SPF_server_set_explanation( spf_server, opts->explanation, &spf_response );
521 if ( err ) {
522 response_print_errors("Error setting default explanation",
523 spf_response, err);
525 }
526 FREE_RESPONSE(spf_response);
527 }
528
529 /*
530 * process the SPF request
531 */
532
533 if (opt_file) {
534 /*
535 * the requests are on STDIN
536 */
537 if (strcmp(opt_file, "-" ) == 0)
538 fin = stdin;
539 else
540 fin = fopen( opt_file, "r" );
541
542 if (!fin) {
543 fprintf( stderr, "Could not open: %s\n", opt_file );
545 }
546 }
547 else {
548 fin = NULL;
549
550 if ((req->ip == NULL) ||
551 (req->sender == NULL && req->helo == NULL) ) {
552 usage();
554 }
555 }
556
557 done_once = FALSE;
558
559 while ( TRUE ) {
560 if ( fin ) {
561 if ( fgets( in_line, sizeof( in_line ), fin ) == NULL )
562 break;
563
564 in_line[strcspn(in_line, "\r\n")] = '\0';
565 p = in_line;
566
567 p += strspn( p, " \t\n" );
568 {
569 if ( *p == '\0' || *p == '#' ) {
570 if ( opt_keep_comments )
571 printf( "%s\n", in_line );
572 continue;
573 }
574 }
575 req->ip = p;
576 p += strcspn( p, " \t\n" );
577 *p++ = '\0';
578
579 p += strspn( p, " \t\n" );
580 req->sender = p;
581 p += strcspn( p, " \t\n" );
582 *p++ = '\0';
583
584 p += strspn( p, " \t\n" );
585 req->helo = p;
586 p += strcspn( p, " \t\n" );
587 *p++ = '\0';
588
589 p += strspn( p, " \t\n" );
590 req->rcpt_to = p;
591 p += strcspn( p, " \t\n" );
592 *p++ = '\0';
593 }
594 else {
595 if ( done_once )
596 break;
597 done_once = TRUE;
598 }
599
600 /* We have to do this here else we leak on CONTINUE_ERROR */
601 FREE_REQUEST(spf_request);
602 FREE_RESPONSE(spf_response);
603
604 spf_request = SPF_request_new(spf_server);
605
606 if (SPF_request_set_ipv4_str(spf_request, req->ip)
607 && SPF_request_set_ipv6_str(spf_request, req->ip)) {
608 printf( "Invalid IP address.\n" );
610 }
611
612 if (req->helo) {
613 if (SPF_request_set_helo_dom( spf_request, req->helo ) ) {
614 printf( "Invalid HELO domain.\n" );
616 }
617 }
618
619 if (SPF_request_set_env_from( spf_request, req->sender ) ) {
620 printf( "Invalid envelope from address.\n" );
622 }
623
624 err = SPF_request_query_mailfrom(spf_request, &spf_response);
625 if (opts->debug)
626 response_print("Main query", spf_response);
627 if (err) {
628 response_print_errors("Failed to query MAIL-FROM",
629 spf_response, err);
631 }
632
633 if (result != NULL)
634 result[0] = '\0';
635 APPEND_RESULT(SPF_response_result(spf_response));
636
637 if (req->rcpt_to != NULL && *req->rcpt_to != '\0' ) {
638 p = req->rcpt_to;
639 p_end = p + strcspn(p, ",;");
640
641 /* This is some incarnation of 2mx mode. */
642 while (SPF_response_result(spf_response)!=SPF_RESULT_PASS) {
643 if (*p_end)
644 *p_end = '\0';
645 else
646 p_end = NULL; /* Note this is last rcpt */
647
648 err = SPF_request_query_rcptto(spf_request,
649 &spf_response_2mx, p);
650 if (opts->debug)
651 response_print("2mx query", spf_response_2mx);
652 if (err) {
653 response_print_errors("Failed to query RCPT-TO",
654 spf_response, err);
656 }
657
658 /* append the result */
659 APPEND_RESULT(SPF_response_result(spf_response_2mx));
660
661 spf_response = SPF_response_combine(spf_response,
662 spf_response_2mx);
663
664 if (!p_end)
665 break;
666 p = p_end + 1;
667 }
668 }
669
670 /* We now have an option to call SPF_request_query_fallback */
671 if (opts->fallback) {
672 err = SPF_request_query_fallback(spf_request,
673 &spf_response_fallback, opts->fallback);
674 if (opts->debug)
675 response_print("fallback query", spf_response_fallback);
676 if (err) {
677 response_print_errors("Failed to query best-guess",
678 spf_response_fallback, err);
680 }
681
682 /* append the result */
683 APPEND_RESULT(SPF_response_result(spf_response_fallback));
684
685 spf_response = SPF_response_combine(spf_response,
686 spf_response_fallback);
687 }
688
689 printf( "%s\n%s\n%s\n%s\n",
690 result,
694 );
695
696 res = SPF_response_result(spf_response);
697
698 fflush(stdout);
699 }
700
701 error:
702 FREE(result, free);
703 FREE_RESPONSE(spf_response);
704 FREE_REQUEST(spf_request);
705 FREE(spf_server, SPF_server_free);
706
707 FREE(req, free);
708 FREE(opts, free);
709
710#ifdef _WIN32
711 SPF_win32_cleanup();
712#endif
713
714 return res;
715}
int getopt_long_only()
int optind
char * optarg
SPF_server_t * SPF_server_new(SPF_server_dnstype_t dnstype, int debug)
Definition spf_server.c:132
@ SPF_DNS_CACHE
Definition spf_server.h:73
SPF_errcode_t SPF_server_set_explanation(SPF_server_t *sp, const char *exp, SPF_response_t **spf_responsep)
Definition spf_server.c:235
void SPF_server_free(SPF_server_t *sp)
Definition spf_server.c:200
SPF_errcode_t SPF_server_set_sanitize(SPF_server_t *sp, int sanitize)
Definition spf_server.c:228
SPF_errcode_t SPF_server_set_localpolicy(SPF_server_t *sp, const char *policy, int use_default_whitelist, SPF_response_t **spf_responsep)
Definition spf_server.c:267
SPF_errcode_t SPF_server_set_rec_dom(SPF_server_t *sp, const char *dom)
Definition spf_server.c:215
#define SPF_LIB_VERSION_PATCH
#define SPF_LIB_VERSION_MINOR
#define SPF_LIB_VERSION_MAJOR
@ SPF_RESULT_PASS
SPF_errcode_t SPF_response_errcode(SPF_response_t *rp)
const char * SPF_response_get_smtp_comment(SPF_response_t *rp)
const char * SPF_response_get_received_spf(SPF_response_t *rp)
const char * SPF_response_get_header_comment(SPF_response_t *rp)
const char * SPF_error_message(SPF_error_t *err)
SPF_error_t * SPF_response_message(SPF_response_t *rp, int idx)
int SPF_response_messages(SPF_response_t *rp)
char SPF_error_errorp(SPF_error_t *err)
SPF_response_t * SPF_response_combine(SPF_response_t *main, SPF_response_t *r2mx)
SPF_errcode_t
@ SPF_E_SUCCESS
SPF_result_t SPF_response_result(SPF_response_t *rp)
SPF_reason_t SPF_response_reason(SPF_response_t *rp)
const char * SPF_strerror(SPF_errcode_t spf_err)
void SPF_get_lib_version(int *major, int *minor, int *patch)
Definition spf_utils.c:41
const char * SPF_strresult(SPF_result_t result)
Definition spf_utils.c:84
const char * SPF_strreason(SPF_reason_t reason)
Definition spf_utils.c:131
A testing layer for DNS.
#define NULL
SPF_request_t * SPF_request_new(SPF_server_t *spf_server)
Definition spf_request.c:41
SPF_errcode_t SPF_request_set_ipv6_str(SPF_request_t *sr, const char *astr)
SPF_errcode_t SPF_request_query_fallback(SPF_request_t *spf_request, SPF_response_t **spf_responsep, const char *record)
int SPF_request_set_env_from(SPF_request_t *sr, const char *from)
SPF_errcode_t SPF_request_set_helo_dom(SPF_request_t *sr, const char *dom)
SPF_errcode_t SPF_request_set_ipv4_str(SPF_request_t *sr, const char *astr)
Definition spf_request.c:95
SPF_errcode_t SPF_request_query_rcptto(SPF_request_t *spf_request, SPF_response_t **spf_responsep, const char *rcpt_to)
SPF_errcode_t SPF_request_query_mailfrom(SPF_request_t *spf_request, SPF_response_t **spf_responsep)
int main(int argc, char *argv[])
Definition spfquery.c:333
#define CONTINUE_ERROR
Definition spfquery.c:122
#define X_OR_EMPTY(x)
Definition spfquery.c:143
#define FREE_RESPONSE(x)
Definition spfquery.c:120
#define FAIL_ERROR
Definition spfquery.c:124
#define APPEND_RESULT(n)
Definition spfquery.c:137
#define WARN_ERROR
Definition spfquery.c:123
#define FREE_REQUEST(x)
Definition spfquery.c:119
#define FREE(x, f)
Definition spfquery.c:118
#define TRUE
Definition spfquery.c:115
#define FALSE
Definition spfquery.c:116
#define SPF_TEST_VERSION
Definition spfquery.c:47
void usage(void)
Definition spfd.c:446
int val
Definition getopt.h:90
char * name
Definition getopt.h:84
int * flag
Definition getopt.h:89
const char * explanation
Definition spfquery.c:316
const char * rec_dom
Definition spfquery.c:318
const char * fallback
Definition spfquery.c:317