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