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 */
83static 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
114int 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 getopt()
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
void SPF_server_free(SPF_server_t *sp)
Definition spf_server.c:200
@ 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)
void SPF_response_free(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_errcode_t SPF_error_code(SPF_error_t *err)
SPF_result_t SPF_response_result(SPF_response_t *rp)
const char * SPF_strerror(SPF_errcode_t spf_err)
const char * SPF_strresult(SPF_result_t result)
Definition spf_utils.c:84
#define NULL
SPF_request_t * SPF_request_new(SPF_server_t *spf_server)
Definition spf_request.c:41
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
void SPF_request_free(SPF_request_t *sr)
Definition spf_request.c:59
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[])
#define VALID_STR(x)