1 /*
2 ** Copyright (C) 2007-2020 by Carnegie Mellon University.
3 **
4 ** @OPENSOURCE_LICENSE_START@
5 ** See license information in ../../LICENSE.txt
6 ** @OPENSOURCE_LICENSE_END@
7 */
8
9 /*
10 *
11 * rwsetmember.c
12 *
13 * Determine whether the IP wildcard specified on the command line
14 * is a member of the specified IPset(s).
15 */
16
17 #include <silk/silk.h>
18
19 RCSIDENT("$SiLK: rwsetmember.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
20
21 #include <silk/skipset.h>
22 #include <silk/skstream.h>
23 #include <silk/utils.h>
24
25
26 /* LOCAL DEFINES AND TYPEDEFS */
27
28 /* where to write output from --help */
29 #define USAGE_FH stdout
30
31
32 /* LOCAL VARIABLES */
33
34 /* The address pattern to be matched */
35 static char *pattern = NULL;
36
37 /* If set to true, no output will be produced */
38 static int quiet = 0;
39
40 /* If true, print a count of how many matches */
41 static int count = 0;
42
43 /* index of first option that is not handled by the options handler. */
44 static int arg_index = 0;
45
46
47 /* OPTIONS SETUP */
48
49 typedef enum {
50 OPT_COUNT,
51 OPT_QUIET
52 } appOptionsEnum;
53
54 static struct option appOptions[] = {
55 {"count", NO_ARG, 0, OPT_COUNT},
56 {"quiet", NO_ARG, 0, OPT_QUIET},
57 {0, 0, 0, 0} /* sentinel entry */
58 };
59
60 static const char *appHelp[] = {
61 "Print count of matches along with filenames",
62 "No output, only set exit status",
63 (char *) NULL
64 };
65
66
67 /* LOCAL FUNCTION PROTOTYPES */
68
69 static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg);
70
71
72 /* FUNCTION DEFINITIONS */
73
74 /*
75 * appUsageLong();
76 *
77 * Print complete usage information to USAGE_FH. Pass this
78 * function to skOptionsSetUsageCallback(); skOptionsParse() will
79 * call this funciton and then exit the program when the --help
80 * option is given.
81 */
82 static void
appUsageLong(void)83 appUsageLong(
84 void)
85 {
86 #define USAGE_MSG \
87 ("[SWITCHES] WILDCARD_IP INPUT_SET [INPUT_SET...]\n" \
88 "\tDetermine existence of IP address(es) in one or more IPset files.\n" \
89 "\tBy default, print names of INPUT_SETs that contain WILDCARD_IP.\n")
90
91 FILE *fh = USAGE_FH;
92
93 skAppStandardUsage(fh, USAGE_MSG, appOptions, appHelp);
94 }
95
96
97 /*
98 * appTeardown()
99 *
100 * Teardown all modules, close all files, and tidy up all
101 * application state.
102 *
103 * This function is idempotent.
104 */
105 static void
appTeardown(void)106 appTeardown(
107 void)
108 {
109 static int teardownFlag = 0;
110
111 if (teardownFlag) {
112 return;
113 }
114 teardownFlag = 1;
115
116 skAppUnregister();
117 }
118
119
120 /*
121 * appSetup(argc, argv);
122 *
123 * Perform all the setup for this application include setting up
124 * required modules, parsing options, etc. This function should be
125 * passed the same arguments that were passed into main().
126 *
127 * Returns to the caller if all setup succeeds. If anything fails,
128 * this function will cause the application to exit with a FAILURE
129 * exit status.
130 */
131 static void
appSetup(int argc,char ** argv)132 appSetup(
133 int argc,
134 char **argv)
135 {
136 SILK_FEATURES_DEFINE_STRUCT(features);
137
138 /* verify same number of options and help strings */
139 assert((sizeof(appHelp) / sizeof(char *)) ==
140 (sizeof(appOptions) / sizeof(struct option)));
141
142 /* register the application */
143 skAppRegister(argv[0]);
144 skAppVerifyFeatures(&features, NULL);
145 skOptionsSetUsageCallback(&appUsageLong);
146
147 /* register the options */
148 if (skOptionsRegister(appOptions, & appOptionsHandler, NULL)) {
149 skAppPrintErr("Unable to register options");
150 exit(EXIT_FAILURE);
151 }
152
153 /* register the teardown hanlder */
154 if (atexit(appTeardown) < 0) {
155 skAppPrintErr("Unable to register appTeardown() with atexit()");
156 appTeardown();
157 exit(EXIT_FAILURE);
158 }
159
160 /* parse options */
161 arg_index = skOptionsParse(argc, argv);
162 if (arg_index < 0) {
163 skAppUsage(); /* never returns */
164 }
165
166 /* get the pattern */
167 pattern = argv[arg_index++];
168 if (NULL == pattern) {
169 skAppPrintErr("No pattern specified");
170 skAppUsage();
171 }
172
173 /* either need name of set file(s) after options or a set file on stdin */
174 if ((arg_index == argc) && (FILEIsATty(stdin))) {
175 skAppPrintErr("No files on the command line and"
176 " stdin is connected to a terminal");
177 skAppUsage();
178 }
179
180 return; /* OK */
181 }
182
183
184 /*
185 * status = appOptionsHandler(cData, opt_index, opt_arg);
186 *
187 * This function is passed to skOptionsRegister(); it will be called
188 * by skOptionsParse() for each user-specified switch that the
189 * application has registered; it should handle the switch as
190 * required---typically by setting global variables---and return 1
191 * if the switch processing failed or 0 if it succeeded. Returning
192 * a non-zero from from the handler causes skOptionsParse() to return
193 * a negative value.
194 *
195 * The clientData in 'cData' is typically ignored; 'opt_index' is
196 * the index number that was specified as the last value for each
197 * struct option in appOptions[]; 'opt_arg' is the user's argument
198 * to the switch for options that have a REQUIRED_ARG or an
199 * OPTIONAL_ARG.
200 */
201 static int
appOptionsHandler(clientData UNUSED (cData),int opt_index,char UNUSED (* opt_arg))202 appOptionsHandler(
203 clientData UNUSED(cData),
204 int opt_index,
205 char UNUSED(*opt_arg))
206 {
207 switch ((appOptionsEnum)opt_index) {
208 case OPT_COUNT:
209 count = 1;
210 break;
211
212 case OPT_QUIET:
213 quiet = 1;
214 break;
215 }
216
217 return 0; /* OK */
218 }
219
220
221 /*
222 * filename = appNextInput(argc, argv);
223 *
224 * Return the name of the next input file from the command line or
225 * the standard input if no files were given on the command line.
226 */
227 static const char *
appNextInput(int argc,char ** argv)228 appNextInput(
229 int argc,
230 char **argv)
231 {
232 static int initialized = 0;
233 const char *fname = NULL;
234
235 if (arg_index < argc) {
236 /* get current file and prepare to get next */
237 fname = argv[arg_index];
238 ++arg_index;
239 } else {
240 if (initialized) {
241 /* no more input */
242 return NULL;
243 }
244 /* input is from stdin */
245 fname = "stdin";
246 }
247
248 initialized = 1;
249 return fname;
250 }
251
252
main(int argc,char ** argv)253 int main(int argc, char **argv)
254 {
255 char errbuf[2 * PATH_MAX];
256 const char *filename = NULL;
257 skstream_t *stream = NULL;
258 skIPWildcard_t ipwild;
259 skipset_t *input_set = NULL;
260 skipset_t *wild_set = NULL;
261 char buf[64];
262 int found_match = 0; /* application return value */
263 int rv;
264
265 appSetup(argc, argv); /* never returns on error */
266
267 /* Build an IP wildcard from the pattern argument */
268 rv = skStringParseIPWildcard(&ipwild, pattern);
269 if (rv) {
270 skAppPrintErr("Invalid IP '%s': %s",
271 pattern, skStringParseStrerror(rv));
272 skAppUsage();
273 }
274
275 if (count && !quiet) {
276 /* Create an IPset containing the IPwildcard */
277 if ((rv = skIPSetCreate(&wild_set, skIPWildcardIsV6(&ipwild)))
278 || (rv = skIPSetInsertIPWildcard(wild_set, &ipwild))
279 || (rv = skIPSetClean(wild_set)))
280 {
281 skAppPrintErr("Unable to create temporary IPset: %s",
282 skIPSetStrerror(rv));
283 return EXIT_FAILURE;
284 }
285 }
286
287 /* Iterate over the set files */
288 while ((filename = appNextInput(argc, argv)) != NULL) {
289 /* Load the input set */
290 if ((rv = skStreamCreate(&stream, SK_IO_READ, SK_CONTENT_SILK))
291 || (rv = skStreamBind(stream, filename))
292 || (rv = skStreamOpen(stream)))
293 {
294 skStreamLastErrMessage(stream, rv, errbuf, sizeof(errbuf));
295 skAppPrintErr("Unable to read IPset from '%s': %s",
296 filename, errbuf);
297 skStreamDestroy(&stream);
298 continue;
299 }
300 rv = skIPSetRead(&input_set, stream);
301 if (rv) {
302 if (SKIPSET_ERR_FILEIO == rv) {
303 skStreamLastErrMessage(stream,
304 skStreamGetLastReturnValue(stream),
305 errbuf, sizeof(errbuf));
306 } else {
307 strncpy(errbuf, skIPSetStrerror(rv), sizeof(errbuf));
308 }
309 skAppPrintErr("Unable to read IPset from '%s': %s",
310 filename, errbuf);
311 skStreamDestroy(&stream);
312 continue;
313 }
314 skStreamDestroy(&stream);
315
316 if (quiet || !count) {
317 /* Only need to check for a match */
318 if (skIPSetCheckIPWildcard(input_set, &ipwild)) {
319 found_match = 1;
320 if (quiet) {
321 goto done;
322 }
323 printf("%s\n", filename);
324 }
325 } else {
326 /* Need a count of IPs, so intersect */
327 rv = skIPSetIntersect(input_set, wild_set);
328 if (rv) {
329 skAppPrintErr("Unable to intersect IPsets: %s",
330 skIPSetStrerror(rv));
331 skIPSetDestroy(&input_set);
332 skIPSetDestroy(&wild_set);
333 return EXIT_FAILURE;
334 }
335
336 printf("%s:%s\n",
337 filename,
338 skIPSetCountIPsString(input_set, buf, sizeof(buf)));
339 if ('0' != buf[0]) {
340 found_match = 1;
341 }
342 }
343
344 skIPSetDestroy(&input_set);
345 }
346
347 done:
348 /* done */
349 skIPSetDestroy(&input_set);
350 skIPSetDestroy(&wild_set);
351
352 return ((found_match) ? 0 : 1);
353 }
354
355
356 /*
357 ** Local Variables:
358 ** mode:c
359 ** indent-tabs-mode:nil
360 ** c-basic-offset:4
361 ** End:
362 */
363