Big cleanup in config help and description
[oweals/busybox.git] / networking / ipcalc.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini ipcalc implementation for busybox
4  *
5  * By Jordan Crouse <jordan@cosmicpenguin.net>
6  *    Stephan Linz  <linz@li-pro.net>
7  *
8  * This is a complete reimplementation of the ipcalc program
9  * from Red Hat.  I didn't look at their source code, but there
10  * is no denying that this is a loving reimplementation
11  *
12  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
13  */
14 //config:config IPCALC
15 //config:       bool "ipcalc"
16 //config:       default y
17 //config:       help
18 //config:         ipcalc takes an IP address and netmask and calculates the
19 //config:         resulting broadcast, network, and host range.
20 //config:
21 //config:config FEATURE_IPCALC_LONG_OPTIONS
22 //config:       bool "Enable long options"
23 //config:       default y
24 //config:       depends on IPCALC && LONG_OPTS
25 //config:
26 //config:config FEATURE_IPCALC_FANCY
27 //config:       bool "Fancy IPCALC, more options, adds 1 kbyte"
28 //config:       default y
29 //config:       depends on IPCALC
30 //config:       help
31 //config:         Adds the options hostname, prefix and silent to the output of
32 //config:         "ipcalc".
33
34 //applet:IF_IPCALC(APPLET(ipcalc, BB_DIR_BIN, BB_SUID_DROP))
35
36 //kbuild:lib-$(CONFIG_IPCALC) += ipcalc.o
37
38 //usage:#define ipcalc_trivial_usage
39 //usage:       "[OPTIONS] ADDRESS"
40 //usage:       IF_FEATURE_IPCALC_FANCY("[/PREFIX]") " [NETMASK]"
41 //usage:#define ipcalc_full_usage "\n\n"
42 //usage:       "Calculate IP network settings from a IP address\n"
43 //usage:        IF_FEATURE_IPCALC_LONG_OPTIONS(
44 //usage:     "\n        -b,--broadcast  Display calculated broadcast address"
45 //usage:     "\n        -n,--network    Display calculated network address"
46 //usage:     "\n        -m,--netmask    Display default netmask for IP"
47 //usage:        IF_FEATURE_IPCALC_FANCY(
48 //usage:     "\n        -p,--prefix     Display the prefix for IP/NETMASK"
49 //usage:     "\n        -h,--hostname   Display first resolved host name"
50 //usage:     "\n        -s,--silent     Don't ever display error messages"
51 //usage:        )
52 //usage:        )
53 //usage:        IF_NOT_FEATURE_IPCALC_LONG_OPTIONS(
54 //usage:     "\n        -b      Display calculated broadcast address"
55 //usage:     "\n        -n      Display calculated network address"
56 //usage:     "\n        -m      Display default netmask for IP"
57 //usage:        IF_FEATURE_IPCALC_FANCY(
58 //usage:     "\n        -p      Display the prefix for IP/NETMASK"
59 //usage:     "\n        -h      Display first resolved host name"
60 //usage:     "\n        -s      Don't ever display error messages"
61 //usage:        )
62 //usage:        )
63
64 #include "libbb.h"
65 /* After libbb.h, because on some systems it needs other includes */
66 #include <arpa/inet.h>
67
68 #define CLASS_A_NETMASK ntohl(0xFF000000)
69 #define CLASS_B_NETMASK ntohl(0xFFFF0000)
70 #define CLASS_C_NETMASK ntohl(0xFFFFFF00)
71
72 static unsigned long get_netmask(unsigned long ipaddr)
73 {
74         ipaddr = htonl(ipaddr);
75
76         if ((ipaddr & 0xC0000000) == 0xC0000000)
77                 return CLASS_C_NETMASK;
78         else if ((ipaddr & 0x80000000) == 0x80000000)
79                 return CLASS_B_NETMASK;
80         else if ((ipaddr & 0x80000000) == 0)
81                 return CLASS_A_NETMASK;
82         else
83                 return 0;
84 }
85
86 #if ENABLE_FEATURE_IPCALC_FANCY
87 static int get_prefix(unsigned long netmask)
88 {
89         unsigned long msk = 0x80000000;
90         int ret = 0;
91
92         netmask = htonl(netmask);
93         while (msk) {
94                 if (netmask & msk)
95                         ret++;
96                 msk >>= 1;
97         }
98         return ret;
99 }
100 #else
101 int get_prefix(unsigned long netmask);
102 #endif
103
104
105 #define NETMASK   0x01
106 #define BROADCAST 0x02
107 #define NETWORK   0x04
108 #define NETPREFIX 0x08
109 #define HOSTNAME  0x10
110 #define SILENT    0x20
111
112 #if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
113         static const char ipcalc_longopts[] ALIGN1 =
114                 "netmask\0"   No_argument "m" // netmask from IP (assuming complete class A, B, or C network)
115                 "broadcast\0" No_argument "b" // broadcast from IP [netmask]
116                 "network\0"   No_argument "n" // network from IP [netmask]
117 # if ENABLE_FEATURE_IPCALC_FANCY
118                 "prefix\0"    No_argument "p" // prefix from IP[/prefix] [netmask]
119                 "hostname\0"  No_argument "h" // hostname from IP
120                 "silent\0"    No_argument "s" // don’t ever display error messages
121 # endif
122                 ;
123 #endif
124
125 int ipcalc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
126 int ipcalc_main(int argc UNUSED_PARAM, char **argv)
127 {
128         unsigned opt;
129         bool have_netmask = 0;
130         struct in_addr s_netmask, s_broadcast, s_network, s_ipaddr;
131         /* struct in_addr { in_addr_t s_addr; }  and  in_addr_t
132          * (which in turn is just a typedef to uint32_t)
133          * are essentially the same type. A few macros for less verbosity: */
134 #define netmask   (s_netmask.s_addr)
135 #define broadcast (s_broadcast.s_addr)
136 #define network   (s_network.s_addr)
137 #define ipaddr    (s_ipaddr.s_addr)
138         char *ipstr;
139
140 #if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
141         applet_long_options = ipcalc_longopts;
142 #endif
143         opt_complementary = "-1:?2"; /* minimum 1 arg, maximum 2 args */
144         opt = getopt32(argv, "mbn" IF_FEATURE_IPCALC_FANCY("phs"));
145         argv += optind;
146         if (opt & SILENT)
147                 logmode = LOGMODE_NONE; /* suppress error_msg() output */
148         opt &= ~SILENT;
149         if (!(opt & (BROADCAST | NETWORK | NETPREFIX))) {
150                 /* if no options at all or
151                  * (no broadcast,network,prefix) and (two args)... */
152                 if (!opt || argv[1])
153                         bb_show_usage();
154         }
155
156         ipstr = argv[0];
157         if (ENABLE_FEATURE_IPCALC_FANCY) {
158                 unsigned long netprefix = 0;
159                 char *prefixstr;
160
161                 prefixstr = ipstr;
162
163                 while (*prefixstr) {
164                         if (*prefixstr == '/') {
165                                 *prefixstr++ = '\0';
166                                 if (*prefixstr) {
167                                         unsigned msk;
168                                         netprefix = xatoul_range(prefixstr, 0, 32);
169                                         netmask = 0;
170                                         msk = 0x80000000;
171                                         while (netprefix > 0) {
172                                                 netmask |= msk;
173                                                 msk >>= 1;
174                                                 netprefix--;
175                                         }
176                                         netmask = htonl(netmask);
177                                         /* Even if it was 0, we will signify that we have a netmask. This allows */
178                                         /* for specification of default routes, etc which have a 0 netmask/prefix */
179                                         have_netmask = 1;
180                                 }
181                                 break;
182                         }
183                         prefixstr++;
184                 }
185         }
186
187         if (inet_aton(ipstr, &s_ipaddr) == 0) {
188                 bb_error_msg_and_die("bad IP address: %s", argv[0]);
189         }
190
191         if (argv[1]) {
192                 if (ENABLE_FEATURE_IPCALC_FANCY && have_netmask) {
193                         bb_error_msg_and_die("use prefix or netmask, not both");
194                 }
195                 if (inet_aton(argv[1], &s_netmask) == 0) {
196                         bb_error_msg_and_die("bad netmask: %s", argv[1]);
197                 }
198         } else {
199                 /* JHC - If the netmask wasn't provided then calculate it */
200                 if (!ENABLE_FEATURE_IPCALC_FANCY || !have_netmask)
201                         netmask = get_netmask(ipaddr);
202         }
203
204         if (opt & NETMASK) {
205                 printf("NETMASK=%s\n", inet_ntoa(s_netmask));
206         }
207
208         if (opt & BROADCAST) {
209                 broadcast = (ipaddr & netmask) | ~netmask;
210                 printf("BROADCAST=%s\n", inet_ntoa(s_broadcast));
211         }
212
213         if (opt & NETWORK) {
214                 network = ipaddr & netmask;
215                 printf("NETWORK=%s\n", inet_ntoa(s_network));
216         }
217
218         if (ENABLE_FEATURE_IPCALC_FANCY) {
219                 if (opt & NETPREFIX) {
220                         printf("PREFIX=%i\n", get_prefix(netmask));
221                 }
222
223                 if (opt & HOSTNAME) {
224                         struct hostent *hostinfo;
225
226                         hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
227                         if (!hostinfo) {
228                                 bb_herror_msg_and_die("can't find hostname for %s", argv[0]);
229                         }
230                         str_tolower(hostinfo->h_name);
231
232                         printf("HOSTNAME=%s\n", hostinfo->h_name);
233                 }
234         }
235
236         return EXIT_SUCCESS;
237 }