"Fossies" - the Fresh Open Source Software Archive 
Member "libspf2-1.2.10/src/spfquery/spfquery.c" (28 Jan 2012, 17113 Bytes) of package /linux/privat/libspf2-1.2.10.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "spfquery.c" see the
Fossies "Dox" file reference documentation.
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
49 #include "libreplace/win32_config.h"
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
145 static 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
171 static void
172 unimplemented(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
189 static void
190 usage()
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
202 static void
203 help()
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
258 static void
259 response_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
291 static void
292 response_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
312 typedef
313 struct SPF_client_options_struct {
314 // void *hook;
315 char *localpolicy;
316 const char *explanation;
317 const char *fallback;
318 const char *rec_dom;
319 int use_trusted;
320 int max_lookup;
321 int sanitize;
322 int debug;
323 } SPF_client_options_t;
324
325 typedef
326 struct SPF_client_request_struct {
327 char *ip;
328 char *sender;
329 char *helo;
330 char *rcpt_to;
331 } SPF_client_request_t;
332
333 int main( int argc, char *argv[] )
334 {
335 SPF_client_options_t *opts;
336 SPF_client_request_t *req;
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." );
372 FAIL_ERROR;
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",
455 SPF_TEST_VERSION );
456 fprintf( stderr, "Compiled with SPF library version: %d.%d.%d\n",
457 SPF_LIB_VERSION_MAJOR, SPF_LIB_VERSION_MINOR,
458 SPF_LIB_VERSION_PATCH );
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();
464 FAIL_ERROR;
465 break;
466
467 case 0:
468 case '?':
469 help();
470 FAIL_ERROR;
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);
486 FAIL_ERROR;
487 }
488 }
489
490 if (optind != argc) {
491 help();
492 FAIL_ERROR;
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);
513 WARN_ERROR;
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);
524 WARN_ERROR;
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 );
544 FAIL_ERROR;
545 }
546 }
547 else {
548 fin = NULL;
549
550 if ((req->ip == NULL) ||
551 (req->sender == NULL && req->helo == NULL) ) {
552 usage();
553 FAIL_ERROR;
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" );
609 CONTINUE_ERROR;
610 }
611
612 if (req->helo) {
613 if (SPF_request_set_helo_dom( spf_request, req->helo ) ) {
614 printf( "Invalid HELO domain.\n" );
615 CONTINUE_ERROR;
616 }
617 }
618
619 if (SPF_request_set_env_from( spf_request, req->sender ) ) {
620 printf( "Invalid envelope from address.\n" );
621 CONTINUE_ERROR;
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);
630 CONTINUE_ERROR;
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);
655 CONTINUE_ERROR;
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);
679 CONTINUE_ERROR;
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,
691 X_OR_EMPTY(SPF_response_get_smtp_comment(spf_response)),
692 X_OR_EMPTY(SPF_response_get_header_comment(spf_response)),
693 X_OR_EMPTY(SPF_response_get_received_spf(spf_response))
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 }