1 /*
2  * Copyright (c) 2008-2011 Internet Initiative Japan Inc. All rights reserved.
3  *
4  * The terms and conditions of the accompanying program
5  * shall be provided separately by Internet Initiative Japan Inc.
6  * Any use, reproduction or distribution of the program are permitted
7  * provided that you agree to be bound to such terms and conditions.
8  *
9  * $Id: sidfquery.c 1426 2011-12-03 17:14:40Z takahiko $
10  */
11 
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15 
16 #include "rcsid.h"
17 RCSID("$Id: sidfquery.c 1426 2011-12-03 17:14:40Z takahiko $");
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sysexits.h>
22 #include <limits.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <stdbool.h>
27 #include <stdarg.h>
28 #include <syslog.h>
29 
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 
34 #include "ptrop.h"
35 #include "dnsresolv.h"
36 #include "sidf.h"
37 
38 bool g_verbose_mode = false;
39 
40 static void
usage(FILE * fp)41 usage(FILE *fp)
42 {
43     fprintf(fp, "\nUsage: sidfquery [-46mpsv] username@domain IP-address1 IP-address2 ...\n\n");
44     fprintf(fp, "handling of IP address:\n");
45     fprintf(fp, "  -4    handle \"IP-address\" as IPv4 address\n");
46     fprintf(fp, "  -6    handle \"IP-address\" as IPv6 address\n\n");
47     fprintf(fp, "evaluation mode:\n");
48     fprintf(fp, "  -s  SPF mode (default)\n");
49     fprintf(fp, "  -m  Sender ID (mfrom) mode\n");
50     fprintf(fp, "  -p  Sender ID (pra) mode\n");
51     fprintf(fp, "features:\n");
52     fprintf(fp, "  -v  verbose mode\n");
53     exit(EX_USAGE);
54 }   // end functiion : usage
55 
56 static void
stdout_logger(int priority,const char * message,...)57 stdout_logger(int priority, const char *message, ...)
58 {
59     if (!g_verbose_mode && LOG_DEBUG == priority) {
60         return;
61     }
62 
63     va_list args;
64     va_start(args, message);
65     vfprintf(stdout, message, args);
66     putc('\n', stdout);
67     va_end(args);
68 }   // end functiion : stdout_logger
69 
70 int
main(int argc,char ** argv)71 main(int argc, char **argv)
72 {
73     int af = AF_UNSPEC;
74     int ai_flags = 0;
75     SidfRecordScope scope = SIDF_RECORD_SCOPE_SPF1;
76 
77     int c;
78     while (-1 != (c = getopt(argc, argv, "46mnpshv"))) {
79         switch (c) {
80         case '4':  // IPv4
81             af = AF_INET;
82             break;
83         case '6':  // IPv6
84             af = AF_INET6;
85             break;
86         case 'm':  // SIDF/mfrom
87             scope = SIDF_RECORD_SCOPE_SPF2_MFROM;
88             break;
89         case 'n':  // to prevent DNS-querying at getaddrinfo()
90             ai_flags |= AI_NUMERICHOST;
91             break;
92         case 'p':  // SIDF/pra
93             scope = SIDF_RECORD_SCOPE_SPF2_PRA;
94             break;
95         case 's':  // SPF
96             scope = SIDF_RECORD_SCOPE_SPF1;
97             break;
98         case 'h':
99             usage(stdout);
100             break;
101         case 'v':
102             g_verbose_mode = true;
103             break;
104         default:
105             fprintf(stdout, "[Error] illegal option: -%c\n", c);
106             usage(stdout);
107             break;
108         }   // end switch
109     }   // end while
110 
111     argc -= optind;
112     argv += optind;
113 
114     if (argc < 2) {
115         usage(stdout);
116     }   // end if
117 
118     DnsResolver *resolver = DnsResolver_new();
119     if (NULL == resolver) {
120         fprintf(stdout, "[Error] resolver initialization failed: error=%s\n", strerror(errno));
121         exit(EX_OSERR);
122     }   // end if
123 
124     const char *mailbox = argv[0];
125 
126     SidfPolicy *policy = SidfPolicy_new();
127     if (NULL == policy) {
128         fprintf(stdout, "[Error] SidfPolicy_new failed: error=%s\n", strerror(errno));
129         exit(EX_OSERR);
130     }   // end if
131     SidfPolicy_setSpfRRLookup(policy, true);
132     SidfPolicy_setLogger(policy, stdout_logger);
133 
134     SidfRequest *request = SidfRequest_new(policy, resolver);
135     if (NULL == request) {
136         fprintf(stdout, "[Error] SidfRequest_new failed: error=%s\n", strerror(errno));
137         exit(EX_OSERR);
138     }   // end if
139 
140     const char *dummy;
141     InetMailbox *envfrom = InetMailbox_build2822Mailbox(mailbox, STRTAIL(mailbox), &dummy, NULL);
142     if (NULL == envfrom) {
143         fprintf(stdout, "[Error] mailbox is not RFC2822 compliant: mailbox=%s\n", mailbox);
144         usage(stdout);
145     }   // end if
146 
147     struct addrinfo ai_hints, *ai_result, *ai_current;
148     memset(&ai_hints, 0, sizeof(struct addrinfo));
149     ai_hints.ai_flags |= ai_flags;
150     ai_hints.ai_family = af;
151     ai_hints.ai_socktype = SOCK_STREAM;
152 
153     for (int i = 1; i < argc; ++i) {
154         int gai_error = getaddrinfo(argv[i], NULL, &ai_hints, &ai_result);
155         if (0 != gai_error) {
156             fprintf(stdout, "[Error] invalid IP address: ip-address=%s, error=%s\n", argv[i],
157                     gai_strerror(gai_error));
158             if (EAI_NONAME == gai_error) {
159                 continue;
160 #ifdef EAI_NODATA
161             } else if (EAI_NODATA == gai_error) {
162                 // some platforms have EAI_NODATA as a return code of getaddrinfo()
163                 continue;
164 #endif  // EAI_NODATA
165             } else {
166                 exit(EX_OSERR);
167             }   // end if
168         }   // end if
169 
170         for (ai_current = ai_result; NULL != ai_current; ai_current = ai_current->ai_next) {
171             if (AF_INET != ai_current->ai_family && AF_INET6 != ai_current->ai_family) {
172                 continue;
173             }   // end if
174 
175             char addr_string[INET6_ADDRSTRLEN];
176             if (AF_INET == ai_current->ai_family) {
177                 (void) inet_ntop(AF_INET, &((struct sockaddr_in *) ai_current->ai_addr)->sin_addr,
178                                  addr_string, sizeof(addr_string));
179             } else {
180                 (void) inet_ntop(AF_INET6,
181                                  &((struct sockaddr_in6 *) ai_current->ai_addr)->sin6_addr,
182                                  addr_string, sizeof(addr_string));
183             }   // end if
184 
185             SidfRequest_reset(request);
186             if (!SidfRequest_setIpAddr(request, ai_current->ai_family, ai_current->ai_addr)) {
187                 fprintf(stdout, "[Error] SidfRequest_setIpAddr failed: address_family=0x%x\n",
188                         ai_current->ai_family);
189                 usage(stdout);
190             }   // end if
191 
192             SidfRequest_setSender(request, envfrom);
193             SidfRequest_setHeloDomain(request, InetMailbox_getDomain(envfrom));
194 
195             // SPF/Sender ID evaluation
196             SidfScore score = SidfRequest_eval(request, scope);
197             const char *spfresultexp = SidfEnum_lookupScoreByValue(score);
198             fprintf(stdout, "%s %s %s\n", mailbox, addr_string, spfresultexp);
199         }   //end for
200 
201         freeaddrinfo(ai_result);
202     }   // end for
203 
204     // clean up
205     InetMailbox_free(envfrom);
206     SidfRequest_free(request);
207     SidfPolicy_free(policy);
208     DnsResolver_free(resolver);
209 
210     exit(EX_OK);
211 }   // end function : main
212