~ubuntu-branches/ubuntu/trusty/grepcidr/trusty-proposed

« back to all changes in this revision

Viewing changes to grepcidr.c

  • Committer: Bazaar Package Importer
  • Author(s): Ryan Finnie
  • Date: 2006-10-05 01:58:17 UTC
  • Revision ID: james.westby@ubuntu.com-20061005015817-yge9lcfrk55azh4d
Tags: upstream-1.3
ImportĀ upstreamĀ versionĀ 1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 
 
3
  grepcidr 1.3 - Filter IP addresses matching IPv4 CIDR specification
 
4
  Copyright (C) 2004, 2005  Jem E. Berkes <jberkes@pc-tools.net>
 
5
        www.sysdesign.ca
 
6
 
 
7
  This program is free software; you can redistribute it and/or modify
 
8
  it under the terms of the GNU General Public License as published by
 
9
  the Free Software Foundation; either version 2 of the License, or
 
10
  (at your option) any later version.
 
11
 
 
12
  This program is distributed in the hope that it will be useful,
 
13
  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
  GNU General Public License for more details.
 
16
 
 
17
  You should have received a copy of the GNU General Public License
 
18
  along with this program; if not, write to the Free Software
 
19
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
 
 
21
*/
 
22
 
 
23
 
 
24
#include <memory.h>
 
25
#include <stdio.h>
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include "getopt.h"
 
29
 
 
30
#define EXIT_OK         0
 
31
#define EXIT_NOMATCH    1
 
32
#define EXIT_ERROR      2
 
33
 
 
34
#define TXT_VERSION     "grepcidr 1.3\nCopyright (C) 2004, 2005  Jem E. Berkes <jberkes@pc-tools.net>\n"
 
35
#define TXT_USAGE       "Usage:\n" \
 
36
                        "\tgrepcidr [-V] [-c] [-v] PATTERN [FILE]\n" \
 
37
                        "\tgrepcidr [-V] [-c] [-v] [-e PATTERN | -f FILE] [FILE]\n"
 
38
#define MAXFIELD        512
 
39
#define TOKEN_SEPS      "\t,\r\n"       /* so user can specify multiple patterns on command line */
 
40
#define INIT_NETWORKS   8192
 
41
 
 
42
/*
 
43
        Specifies a network. Whether originally in CIDR format (IP/mask)
 
44
        or a range of IPs (IP_start-IP_end), spec is converted to a range.
 
45
        The range is min to max (32-bit IPs) inclusive.
 
46
*/
 
47
struct netspec
 
48
{
 
49
        unsigned int min;
 
50
        unsigned int max;
 
51
};
 
52
 
 
53
/* Macro to test for valid IP address in four integers */
 
54
#define VALID_IP(IP) ((IP[0]<256) && (IP[1]<256) && (IP[2]<256) && (IP[3]<256))
 
55
/* Macro to build 32-bit IP from four integers */
 
56
#define BUILD_IP(IP) ((IP[0]<<24) | (IP[1]<<16) | (IP[2]<<8) | IP[3])
 
57
 
 
58
 
 
59
/* Global variables */
 
60
unsigned int patterns = 0;              /* total patterns in array */
 
61
unsigned int capacity = 0;              /* current capacity of array */
 
62
struct netspec* array = NULL;           /* array of patterns, network specs */
 
63
 
 
64
/*
 
65
        Insert new spec inside array of network spec
 
66
        Dynamically grow array buffer as needed
 
67
        The array must have already been initially allocated, with valid capacity
 
68
*/
 
69
void array_insert(struct netspec* newspec)
 
70
{
 
71
        if (patterns == capacity)
 
72
        {
 
73
                capacity *= 2;
 
74
                array = realloc(array, capacity*sizeof(struct netspec));
 
75
        }
 
76
        array[patterns++] = *newspec;
 
77
}
 
78
 
 
79
 
 
80
/*
 
81
        Convert IP address string to 32-bit integer version
 
82
        Returns 0 on failure
 
83
*/
 
84
unsigned int ip_to_uint(const char* ip)
 
85
{
 
86
        unsigned int IP[4];     /* 4 octets for IP address */
 
87
        if ((sscanf(ip, "%u.%u.%u.%u", &IP[0], &IP[1], &IP[2], &IP[3]) == 4) && VALID_IP(IP))
 
88
                return BUILD_IP(IP);
 
89
        else
 
90
                return 0;
 
91
}
 
92
 
 
93
 
 
94
/*
 
95
        Given string, fills in the struct netspec (must be allocated)
 
96
        Accept CIDR IP/mask format or IP_start-IP_end range.
 
97
        Returns true (nonzero) on success, false (zero) on failure.
 
98
*/
 
99
int net_parse(const char* line, struct netspec* spec)
 
100
{
 
101
        unsigned int IP1[4], IP2[4];
 
102
        int maskbits = 32;      /* if using CIDR IP/mask format */
 
103
        
 
104
        /* Try parsing IP/mask, CIDR format */
 
105
        if (strchr(line, '/') && (sscanf(line, "%u.%u.%u.%u/%d", &IP1[0], &IP1[1], &IP1[2], &IP1[3], &maskbits) == 5)
 
106
                && VALID_IP(IP1) && (maskbits >= 1) && (maskbits <= 32))
 
107
        {
 
108
                spec->min = BUILD_IP(IP1) & (~((1 << (32-maskbits))-1) & 0xFFFFFFFF);
 
109
                spec->max = spec->min | (((1 << (32-maskbits))-1) & 0xFFFFFFFF);
 
110
                return 1;
 
111
        }
 
112
        /* Try parsing a range. Spaces around hyphen are optional. */
 
113
        else if (strchr(line, '-') && (sscanf(line, "%u.%u.%u.%u - %u.%u.%u.%u", &IP1[0], &IP1[1], &IP1[2], &IP1[3],
 
114
                &IP2[0], &IP2[1], &IP2[2], &IP2[3]) == 8) && VALID_IP(IP1) && VALID_IP(IP2))
 
115
        {
 
116
                spec->min = BUILD_IP(IP1);
 
117
                spec->max = BUILD_IP(IP2);
 
118
                if (spec->max >= spec->min)
 
119
                        return 1;
 
120
                else
 
121
                        return 0;
 
122
        }
 
123
        /* Try simple IP address */
 
124
        else if ((sscanf(line, "%u.%u.%u.%u", &IP1[0], &IP1[1], &IP1[2], &IP1[3]) == 4) && VALID_IP(IP1))
 
125
        {
 
126
                spec->min = BUILD_IP(IP1);
 
127
                spec->max = spec->min;
 
128
                return 1;
 
129
        }
 
130
        return 0;       /* could not parse */
 
131
}
 
132
 
 
133
 
 
134
/* Compare two netspecs, for sorting. Comparison is done on minimum of range */
 
135
int netsort(const void* a, const void* b)
 
136
{
 
137
        unsigned int c1 = ((struct netspec*)a)->min;
 
138
        unsigned int c2 = ((struct netspec*)b)->min;
 
139
        if (c1 < c2) return -1;
 
140
        if (c1 > c2) return +1;
 
141
        return 0;
 
142
}
 
143
 
 
144
/* Compare two netspecs, for searching. Test if key (only min) is inside range */
 
145
int netsearch(const void* a, const void* b)
 
146
{
 
147
        unsigned int key = ((struct netspec*)a)->min;
 
148
        unsigned int min = ((struct netspec*)b)->min;
 
149
        unsigned int max = ((struct netspec*)b)->max;
 
150
        if (key < min) return -1;
 
151
        if (key > max) return +1;
 
152
        return 0;
 
153
}
 
154
 
 
155
 
 
156
int main(int argc, char* argv[])
 
157
{
 
158
        static char shortopts[] = "ce:f:vV";
 
159
        FILE* inp_stream = stdin;               /* input stream, list of IPs to match */
 
160
        char* pat_filename = NULL;              /* filename containing patterns */
 
161
        char* pat_strings = NULL;               /* pattern strings on command line */
 
162
        unsigned int counting = 0;              /* when non-zero, counts matches */
 
163
        int invert = 0;                         /* flag for inverted mode */
 
164
        char line[MAXFIELD];
 
165
        int foundopt;
 
166
        int anymatch = 0;                       /* did anything match? for exit code */
 
167
 
 
168
        if (argc == 1)
 
169
        {
 
170
                fprintf(stderr, TXT_USAGE);
 
171
                return EXIT_ERROR;
 
172
        }
 
173
 
 
174
        while ((foundopt = getopt(argc, argv, shortopts)) != -1)
 
175
        {
 
176
                switch (foundopt)
 
177
                {
 
178
                        case 'V':
 
179
                                puts(TXT_VERSION);
 
180
                                return EXIT_ERROR;
 
181
                                
 
182
                        case 'c':
 
183
                                counting = 1;
 
184
                                break;
 
185
                                
 
186
                        case 'v':
 
187
                                invert = 1;
 
188
                                break;
 
189
                                
 
190
                        case 'e':
 
191
                                pat_strings = optarg;
 
192
                                break;
 
193
 
 
194
                        case 'f':
 
195
                                pat_filename = optarg;
 
196
                                break;
 
197
                                
 
198
                        default:
 
199
                                fprintf(stderr, TXT_USAGE);
 
200
                                return EXIT_ERROR;
 
201
                }
 
202
        }
 
203
        
 
204
        if (!pat_filename && !pat_strings)
 
205
        {
 
206
                if (optind < argc)
 
207
                        pat_strings = argv[optind++];
 
208
                else
 
209
                {
 
210
                        fprintf(stderr, "Specify PATTERN or -f FILE to read patterns from\n");
 
211
                        return EXIT_ERROR;
 
212
                }
 
213
        }
 
214
        
 
215
        if (optind < argc)
 
216
        {
 
217
                inp_stream = fopen(argv[optind], "r");
 
218
                if (!inp_stream)
 
219
                {
 
220
                        perror(argv[optind]);
 
221
                        return EXIT_ERROR;
 
222
                }               
 
223
        }
 
224
        
 
225
        /* Initial array allocation */
 
226
        capacity = INIT_NETWORKS;
 
227
        array = (struct netspec*) malloc(capacity*sizeof(struct netspec));
 
228
        
 
229
        /* Load patterns defining networks */
 
230
        if (pat_filename)
 
231
        {
 
232
                FILE* data = fopen(pat_filename, "r");
 
233
                if (data)
 
234
                {
 
235
                        while (fgets(line, sizeof(line), data))
 
236
                        {
 
237
                                struct netspec spec;
 
238
                                if ((*line != '#') && net_parse(line, &spec))
 
239
                                        array_insert(&spec);
 
240
                        }
 
241
                        fclose(data);
 
242
                }
 
243
                else
 
244
                {
 
245
                        perror(pat_filename);
 
246
                        return EXIT_ERROR;
 
247
                }
 
248
        }
 
249
        if (pat_strings)
 
250
        {
 
251
                char* token = strtok(pat_strings, TOKEN_SEPS);
 
252
                while (token)
 
253
                {
 
254
                        struct netspec spec;
 
255
                        if (net_parse(token, &spec))
 
256
                                array_insert(&spec);
 
257
                        token = strtok(NULL, TOKEN_SEPS);
 
258
                }
 
259
        }
 
260
        
 
261
        /* Prepare array for rapid searching */
 
262
        {
 
263
                unsigned int item;
 
264
                qsort(array, patterns, sizeof(struct netspec), netsort);
 
265
                /* cure overlaps so that ranges are disjoint and consistent */
 
266
                for (item=1; item<patterns; item++)
 
267
                {
 
268
                        if (array[item].max <= array[item-1].max)
 
269
                                array[item] = array[item-1];
 
270
                        else if (array[item].min <= array[item-1].max)
 
271
                                array[item].min = array[item-1].max + 1;        /* overflow possibility */
 
272
                }
 
273
        }
 
274
        
 
275
        /* Match IPs from input stream to network patterns */
 
276
        while (fgets(line, sizeof(line), inp_stream))
 
277
        {
 
278
                struct netspec key;
 
279
                if ((key.min=ip_to_uint(line)))
 
280
                {
 
281
                        int match = 0;
 
282
                        if (bsearch(&key, array, patterns, sizeof(struct netspec), netsearch))
 
283
                                match = 1;
 
284
                        if (invert ^ match)
 
285
                        {
 
286
                                anymatch = 1;
 
287
                                if (counting)
 
288
                                        counting++;
 
289
                                else
 
290
                                        printf("%s", line);
 
291
                        }
 
292
                }
 
293
        }
 
294
        
 
295
        /* Cleanup */
 
296
        if (inp_stream != stdin)
 
297
                fclose(inp_stream);
 
298
        if (array)
 
299
                free(array);
 
300
 
 
301
        if (counting)
 
302
                printf("%u\n", counting-1);
 
303
        if (anymatch)
 
304
                return EXIT_OK;
 
305
        else
 
306
                return EXIT_NOMATCH;
 
307
}