1 /*
2  * Copyright (C) 2001-2003  Daniel Kelly
3  * Copyright (C) 2009 - 2011, 2013, 2016, 2018  Peter Pentchev
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 /* program that prints IP address ranges */
21 
22 #include <err.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include "prips.h"
29 #include "except.h"
30 
31 typedef enum {
32 	FORMAT_HEX = 0,
33 	FORMAT_DEC = 1,
34 	FORMAT_DOT = 2
35 } AddrFormat;
36 
37 #define FORMATS "hex", "dec", "dot"
38 
39 #define VERSION_NUMBER	"1.1.1"
40 
41 const char * const MAINTAINER = "Peter Pentchev <roam@ringlet.net>";
42 const char * const VERSION =
43 "prips " VERSION_NUMBER "\n"
44 "This program comes with NO WARRANTY,\n"
45 "to the extent permitted by law.\n"
46 "You may redistribute copies under \n"
47 "the terms of the GNU General Public License.";
48 
49 static void usage(bool error, const char *prog);
50 static AddrFormat get_format(const char *format);
51 
main(const int argc,char * const argv[])52 int main(const int argc, char * const argv[])
53 {
54 	int ch;
55 	const char * const argstr = "d:hf:i:e:cv-:";
56 	uint32_t start = 0, end = 0;
57 	int octet[4][256];	/* Holds all of the exceptions if -e is used */
58 	int format = FORMAT_DOT;	/* Dotted decimal by default */
59 	int delimiter = 10; 	/* New line by default */
60 	int increment = 1;	/* Standard incrementer is one */
61 
62 	/* flags */
63 	bool exception_flag = false;	/* If one, check for exclusions */
64 	bool print_as_cidr_flag= false;	/* If one, print range as addr/offset */
65 	bool version_flag = false;
66 	bool usage_flag = false;
67 	bool features_flag = false;
68 
69 	opterr = 0;
70         while ((ch = getopt(argc, argv, argstr)) != EOF) {
71                 switch (ch) {
72                 case 'c':
73 			print_as_cidr_flag = true;
74 			break;
75                 case 'd':
76 			delimiter = atoi(optarg);
77 
78 			if(delimiter < 0 || delimiter > 255)
79 				errx(1, "delimiter must be between 0 and 255");
80                         break;
81 		case 'f':
82 			format = get_format(optarg);
83 			break;
84 		case 'h':
85 			usage_flag = true;
86 			break;
87 		case 'i':
88 			if((increment = atoi(optarg)) < 1)
89 				errx(1, "increment must be a positive integer");
90 			break;
91 		case 'e':
92 			set_exceptions(optarg, octet);
93 			exception_flag = true;
94 			break;
95 		case 'v':
96 			version_flag = true;
97 			break;
98 		case '-':
99 			if (strcmp(optarg, "help") == 0)
100 				usage_flag = true;
101 			else if (strcmp(optarg, "version") == 0)
102 				version_flag = true;
103 			else if (strcmp(optarg, "features") == 0)
104 				features_flag = true;
105 			else
106 				usage(true, argv[0]);
107 			break;
108 		case '?':
109 			usage(true, argv[0]);
110 		}
111 	}
112 	if (version_flag)
113 		puts(VERSION);
114 	if (usage_flag)
115 		usage(false, argv[0]);
116 	if (features_flag)
117 		puts("Features: prips=" VERSION_NUMBER);
118 	if (version_flag || usage_flag || features_flag)
119 		return 0;
120 
121 	/*************************************************************/
122 	/* Figure out what we have to work with.  argc - optind is   */
123  	/* the number of arguments we have.  If there is one argu-   */
124 	/* ment, we are dealing with CIDR, or a mistake.  If there   */
125 	/* are two arguments, we are dealing with start address and  */
126 	/* end address.  We also make sure to test if the CIDR nota- */
127 	/* tion is proper.                                           */
128 	/*************************************************************/
129 
130 	switch(argc - optind)
131 	{
132 	case 1: /* CIDR? */
133 		{
134 		const char * const prefix = strtok(argv[optind], "/");
135 		const char * const offset = strtok(NULL, "/");
136 		if(offset) /* CIDR */
137 		{
138 			start = numberize(prefix);
139 			if(start == (uint32_t)-1)
140 				errx(1, "bad IP address");
141 			end = add_offset(prefix, atoi(offset));
142 		}
143 		else
144 		{
145 			usage(true, argv[0]);
146 		}
147 		}
148 		break;
149 	case 2: /* start address, end address */
150 	        start = numberize(argv[optind]);
151                 end = numberize(argv[optind+1]);
152 		if(start == (uint32_t)-1 || end == (uint32_t)-1)
153 			errx(1, "bad IP address");
154                 break;
155 	default:
156 		usage(true, argv[0]);
157 	}
158 
159 	/***************************************************************/
160 	/* OK- at this point we have the start and end address.  If    */
161 	/* the start is greater than the end, we exit with an error.   */
162 	/* Otherwise, we start printing addresses that are not part of */
163 	/* the exception list, if one exists.                          */
164 	/***************************************************************/
165 
166 	if(start > end)
167 	{
168 		errx(1, "start address must be smaller than end address");
169 	}
170 
171 	if(print_as_cidr_flag) /* print start - end as start/offset */
172 		printf("%s%c", cidrize(start, end), delimiter);
173 	else
174 	{
175 		for(uint32_t current = start; current <= end; current += increment)
176 		{	if(!exception_flag || !except(&current, octet, increment))
177 			{
178 				switch(format)
179 				{
180 				case FORMAT_HEX:
181 					printf("%lx%c", (long)current, delimiter);
182 					break;
183 				case FORMAT_DEC:
184 					printf("%lu%c", (unsigned long)current, delimiter);
185 					break;
186 				default:
187 					printf("%s%c", denumberize(current),
188 						delimiter);
189 					break;
190 				}
191 			}
192 		}
193 	}
194 	return(0);
195 } /* end main */
196 
usage(bool error,const char * prog)197 static void usage(bool error, const char *prog)
198 {
199 	fprintf(error ? stderr : stdout,
200 	"usage: %s [options] <start end | CIDR block>\n\
201 	-c		print range in CIDR notation\n\
202 	-d <x>		set the delimiter to the character with ASCII code 'x'\n\
203 			where 0 <= x <= 255 \n\
204 	-h		display this help message and exit \n\
205 	-f <x> 		set the format of addresses (hex, dec, or dot)\n\
206 	-i <x>		set the increment to 'x'\n\
207 	-e <x.x.x,x.x>	exclude a range from the output, e.g. -e ..4. will\n\
208 			not print 192.168.4.[0-255]\n\
209 	\n\
210 	%s --help | --version | --features\n\
211 	\n\
212 	\rReport bugs to %s\n",
213 			prog, prog, MAINTAINER);
214 	if (error)
215 		exit(1);
216 }
217 
get_format(const char * const format)218 static AddrFormat get_format(const char * const format)
219 {
220 	const char * const list[] = {FORMATS};
221 
222 	for (size_t i = 0; i < sizeof(list) / sizeof(list[0]); i++)
223 		if( strcmp(format, list[i]) == 0)
224 			return(i);
225 	errx(1, "invalid format '%s'", format);
226 }
227