libspf2  1.2.11
spf_example.c
Go to the documentation of this file.
1 /*
2  * spf_example - An example program for how to use libspf2
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 in the public domain, there is no copyright, you
11  * can do anything you want with it.
12  */
13 
14 
15 /*
16  * The libspf2 library uses the GNU autoconf system to help make
17  * the library more portable. The config.h file should have the
18  * HAVE_xxx defines that are appropriate for your system. Either use
19  * autconf to create it, or create it by hand.
20  */
21 
22 
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 
27 #ifdef STDC_HEADERS
28 # include <stdio.h>
29 # include <stdlib.h> /* malloc / free */
30 #endif
31 
32 #ifdef HAVE_SYS_TYPES_H
33 #include <sys/types.h> /* types (u_char .. etc..) */
34 #endif
35 
36 #ifdef HAVE_INTTYPES_H
37 #include <inttypes.h>
38 #endif
39 
40 #ifdef HAVE_STRING_H
41 # include <string.h> /* strstr / strdup */
42 #else
43 # ifdef HAVE_STRINGS_H
44 # include <strings.h> /* strstr / strdup */
45 # endif
46 #endif
47 
48 #ifdef HAVE_SYS_SOCKET_H
49 # include <sys/socket.h> /* inet_ functions / structs */
50 #endif
51 #ifdef HAVE_NETINET_IN_H
52 # include <netinet/in.h> /* inet_ functions / structs */
53 #endif
54 #ifdef HAVE_ARPA_INET_H
55 # include <arpa/inet.h> /* in_addr struct */
56 #endif
57 
58 #ifdef HAVE_ARPA_NAMESER_H
59 # include <arpa/nameser.h> /* DNS HEADER struct */
60 #endif
61 
62 #ifdef HAVE_UNISTD_H
63 #include <unistd.h>
64 #endif
65 
66 #ifdef HAVE_GETOPT_H
67 #include <getopt.h>
68 #endif
69 
70 
71 
72 /*
73  * libspf2 public include files that are needed for this example
74  * program
75  */
76 
77 #include "spf.h"
78 
79 
80 /*
81  * usage() just prints out the command line options for this program
82  */
83 static void usage()
84 {
85  fprintf(
86  stderr,
87  "Usage:\n"
88  "\n"
89  "spf_example [options]\n"
90  "\n"
91  "Valid data options are:\n"
92  " -i <IP address> The IP address that is sending email\n"
93  " -s <email address> The email address used as the\n"
94  " envelope-from. If no username (local\n"
95  " part) is given, 'postmaster' will be\n"
96  " assumed.\n"
97  " -r <email address> [optional] The email address used as\n"
98  " the envelope-to email address, for\n"
99  " secondary-MX checking.\n"
100  " -h <domain name> The domain name given on the SMTP HELO\n"
101  " command. This is only needed if the\n"
102  " -sender option is not given.\n"
103  " -d [debug level] debug level.\n"
104  );
105 }
106 
107 
108 
109 /*
110  * All the code is in the main routine, but most usages of libspf2
111  * would have the code spread around into various subrotines.
112  */
113 
114 int main( int argc, char *argv[] )
115 {
116  int c;
117  int res = 0;
118  int i;
119 
120  char *opt_ip = NULL;
121  char *opt_sender = NULL;
122  char *opt_helo = NULL;
123  char *opt_rcpt_to = NULL;
124  int opt_debug = 0;
125 
126  /* You should not indirect on any of these structures, as their
127  * layout may change between versions of the library. Use the
128  * accessor functions instead. Definitions of the structs may not
129  * even be provided. */
130 
131  SPF_server_t *spf_server = NULL;
132  SPF_request_t *spf_request = NULL;
133  SPF_response_t *spf_response = NULL;
134  SPF_response_t *spf_response_2mx = NULL;
135 
136 
137  /*
138  * check the arguments
139  */
140 
141  while (1)
142  {
143  c = getopt(argc, argv, "i:s:h:r:d::" );
144 
145  if (c == -1)
146  break;
147 
148  switch (c)
149  {
150  case 'i':
151  opt_ip = optarg;
152  break;
153 
154  case 's':
155  opt_sender = optarg;
156  break;
157 
158  case 'h':
159  opt_helo = optarg;
160  break;
161 
162  case 'r':
163  opt_rcpt_to = optarg;
164  break;
165 
166  case 0:
167  case '?':
168  usage();
169  res = 255;
170  goto error;
171  break;
172 
173  case 'd':
174  if (optarg == NULL)
175  opt_debug = 1;
176  else
177  opt_debug = atoi( optarg );
178  break;
179 
180  default:
181  fprintf( stderr, "Error: getopt returned character code 0%o ??\n", c);
182  }
183  }
184 
185  if (optind != argc
186  || opt_ip == NULL
187  || (opt_helo == NULL && opt_sender == NULL))
188  {
189  usage();
190  res = 255;
191  goto error;
192  }
193 
194 /*
195  * Configure the SPF system.
196  *
197  * libspf2 is designed so that configurations can be set up once
198  * and reused many times different emails delivered in a single SMTP
199  * session or in different SMTP sessions.
200  */
201 
202  /*
203  * set up the SPF server
204  *
205  * Configurations contain malloc'd data so must be
206  * destroyed when you are finished.
207  */
208 
209  spf_server = SPF_server_new(SPF_DNS_CACHE, 1);
210 
211  if (spf_server == NULL) {
212  fprintf( stderr, "SPF_create_config failed.\n" );
213  res = 255;
214  goto error;
215  }
216 
217  /*
218  * Create a new request.
219  *
220  * The SPF request contains all the data needed to process
221  * the SPF check. Requests are malloc'd so it must be
222  * destroyed when you are finished with it.
223  */
224 
225  spf_request = SPF_request_new(spf_server);
226 
227  /* The domain name of the receiving MTA will default to gethostname() */
228  /* SPF_request_set_rec_dom( spf_request, opt_name ); */
229 
230 
231 /*
232  * process the SPF request
233  *
234  * Now that the SPF system has been configured, we can process the requests.
235  * There would normally be a loop around this code or it would be placed
236  * in a subroutine to be called for each email.
237  *
238  * If a single email session sends several emails, you don't need to
239  * reset the IP address or the HELO domain each time, just change the
240  * envelope from.
241  */
242 
243  /*
244  * record the IP address of the client (sending) MTA.
245  *
246  * There are other SPF_set_ip*() functionx if you have a structure
247  * instead of a string.
248  */
249 
250  if ( SPF_request_set_ipv4_str( spf_request, opt_ip ) ) {
251  printf( "Invalid IP address.\n" );
252  res = 255;
253  goto error;
254  }
255 
256 
257  /*
258  * record the HELO domain name of the client (sending) MTA from
259  * the SMTP HELO or EHLO commands
260  *
261  * This domain name will be used if the envelope from address is
262  * null (e.g. MAIL FROM:<>). This happens when a bounce is being
263  * sent and, in effect, it is the client MTA that is sending the
264  * message.
265  */
266 
267  if ( SPF_request_set_helo_dom( spf_request, opt_helo ) ) {
268  printf( "Invalid HELO domain.\n" );
269  res = 255;
270  goto error;
271  }
272 
273  /*
274  * record the envelope from email address from the SMTP MAIL FROM:
275  * command.
276  */
277 
278  if ( SPF_request_set_env_from( spf_request, opt_sender ) ) {
279  printf( "Invalid envelope from address.\n" );
280  res = 255;
281  goto error;
282  }
283 
284  /*
285  * now that we have all the information, see what the result of
286  * the SPF check is.
287  */
288 
289  SPF_request_query_mailfrom(spf_request, &spf_response);
290 
291  /*
292  * If the sender MAIL FROM check failed, then for each SMTP RCPT TO
293  * command, the mail might have come from a secondary MX for that
294  * domain.
295  *
296  * Note that most MTAs will also check the RCPT TO command to make sure
297  * that it is ok to accept. This SPF check won't give a free pass
298  * to all secondary MXes from all domains, just the one specified by
299  * the rcpt_to address. It is assumed that the MTA checks (at some
300  * point) that we are also a valid primary or secondary for the domain.
301  */
302  if (SPF_response_result(spf_response) != SPF_RESULT_PASS) {
303  SPF_request_query_rcptto(spf_request, &spf_response_2mx, opt_rcpt_to);
304  /*
305  * We might now have a PASS if the mail came from a client which
306  * is a secondary MX from the domain specified in opt_rcpt_to.
307  *
308  * If not, then the RCPT TO: address must have been a domain for
309  * which the client is not a secondary MX, AND the MAIL FROM: domain
310  * doesn't doesn't return 'pass' from SPF_result()
311  */
312  if (SPF_response_result(spf_response_2mx) == SPF_RESULT_PASS) {
313  }
314  }
315 
316  /*
317  * If the result is something like 'neutral', you probably
318  * want to accept the email anyway, just like you would
319  * when SPF_result() returns 'neutral'.
320  *
321  * It is possible that you will completely ignore the results
322  * until the SMPT DATA command.
323  */
324 
325  if ( opt_debug > 0 ) {
326  printf ( "result = %s (%d)\n",
327  SPF_strresult(SPF_response_result(spf_response)),
328  SPF_response_result(spf_response));
329  printf ( "err = %s (%d)\n",
330  SPF_strerror(SPF_response_errcode(spf_response)),
331  SPF_response_errcode(spf_response));
332  for (i = 0; i < SPF_response_messages(spf_response); i++) {
333  SPF_error_t *err = SPF_response_message(spf_response, i);
334  printf ( "%s_msg = (%d) %s\n",
335  (SPF_error_errorp(err) ? "warn" : "err"),
336  SPF_error_code(err),
337  SPF_error_message(err));
338  }
339  }
340 
341 #define VALID_STR(x) (x ? x : "")
342 
343  printf( "%s\n%s\n%s\n%s\n",
344  SPF_strresult( SPF_response_result(spf_response) ),
348  );
349 
350  res = SPF_response_result(spf_response);
351 
352 
353  /*
354  * The response from the SPF check contains malloced data, so
355  * make sure we free it.
356  */
357 
358  SPF_response_free(spf_response);
359  if (spf_response_2mx)
360  SPF_response_free(spf_response_2mx);
361 
362  error:
363 
364  /*
365  * the SPF configuration variables contain malloced data, so we
366  * have to vfree them also.
367  */
368 
369  if (spf_request)
370  SPF_request_free(spf_request);
371  if (spf_server)
372  SPF_server_free(spf_server);
373  return res;
374 }
int SPF_response_messages(SPF_response_t *rp)
Definition: spf_response.c:290
void SPF_request_free(SPF_request_t *sr)
Definition: spf_request.c:59
int getopt()
const char * SPF_strerror(SPF_errcode_t spf_err)
Definition: spf_strerror.c:33
char SPF_error_errorp(SPF_error_t *err)
Definition: spf_response.c:326
SPF_request_t * SPF_request_new(SPF_server_t *spf_server)
Definition: spf_request.c:41
SPF_errcode_t SPF_error_code(SPF_error_t *err)
Definition: spf_response.c:314
#define NULL
Definition: spf_internal.h:28
int optind
SPF_errcode_t SPF_request_query_mailfrom(SPF_request_t *spf_request, SPF_response_t **spf_responsep)
Definition: spf_request.c:270
void SPF_response_free(SPF_response_t *rp)
Definition: spf_response.c:53
void usage(void)
Definition: spfd.c:446
const char * SPF_response_get_smtp_comment(SPF_response_t *rp)
Definition: spf_response.c:171
void SPF_server_free(SPF_server_t *sp)
Definition: spf_server.c:200
char * optarg
int SPF_request_set_env_from(SPF_request_t *sr, const char *from)
Definition: spf_request.c:139
SPF_errcode_t SPF_request_query_rcptto(SPF_request_t *spf_request, SPF_response_t **spf_responsep, const char *rcpt_to)
Definition: spf_request.c:340
const char * SPF_error_message(SPF_error_t *err)
Definition: spf_response.c:320
SPF_server_t * SPF_server_new(SPF_server_dnstype_t dnstype, int debug)
Definition: spf_server.c:132
const char * SPF_response_get_header_comment(SPF_response_t *rp)
Definition: spf_response.c:165
const char * SPF_strresult(SPF_result_t result)
Definition: spf_utils.c:81
SPF_errcode_t SPF_request_set_ipv4_str(SPF_request_t *sr, const char *astr)
Definition: spf_request.c:95
const char * SPF_response_get_received_spf(SPF_response_t *rp)
Definition: spf_response.c:153
SPF_errcode_t SPF_request_set_helo_dom(SPF_request_t *sr, const char *dom)
Definition: spf_request.c:117
#define VALID_STR(x)
SPF_result_t SPF_response_result(SPF_response_t *rp)
Definition: spf_response.c:135
SPF_errcode_t SPF_response_errcode(SPF_response_t *rp)
Definition: spf_response.c:147
int main(int argc, char *argv[])
Definition: spf_example.c:114
SPF_error_t * SPF_response_message(SPF_response_t *rp, int idx)
Definition: spf_response.c:308