78558495ece4d48f85ecf7544504be7e39475578
[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 tarball for details.
13  */
14
15 #include <sys/socket.h>
16 #include <arpa/inet.h>
17
18 #include "libbb.h"
19
20 #define CLASS_A_NETMASK ntohl(0xFF000000)
21 #define CLASS_B_NETMASK ntohl(0xFFFF0000)
22 #define CLASS_C_NETMASK ntohl(0xFFFFFF00)
23
24 static unsigned long get_netmask(unsigned long ipaddr)
25 {
26         ipaddr = htonl(ipaddr);
27
28         if ((ipaddr & 0xC0000000) == 0xC0000000)
29                 return CLASS_C_NETMASK;
30         else if ((ipaddr & 0x80000000) == 0x80000000)
31                 return CLASS_B_NETMASK;
32         else if ((ipaddr & 0x80000000) == 0)
33                 return CLASS_A_NETMASK;
34         else
35                 return 0;
36 }
37
38 #if ENABLE_FEATURE_IPCALC_FANCY
39 static int get_prefix(unsigned long netmask)
40 {
41         unsigned long msk = 0x80000000;
42         int ret = 0;
43
44         netmask = htonl(netmask);
45         while (msk) {
46                 if (netmask & msk)
47                         ret++;
48                 msk >>= 1;
49         }
50         return ret;
51 }
52 #else
53 int get_prefix(unsigned long netmask);
54 #endif
55
56
57 #define NETMASK   0x01
58 #define BROADCAST 0x02
59 #define NETWORK   0x04
60 #define NETPREFIX 0x08
61 #define HOSTNAME  0x10
62 #define SILENT    0x20
63
64 #if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
65         static const char ipcalc_longopts[] ALIGN1 =
66                 "netmask\0"   No_argument "m"
67                 "broadcast\0" No_argument "b"
68                 "network\0"   No_argument "n"
69 # if ENABLE_FEATURE_IPCALC_FANCY
70                 "prefix\0"    No_argument "p"
71                 "hostname\0"  No_argument "h"
72                 "silent\0"    No_argument "s"
73 # endif
74                 ;
75 #endif
76
77 int ipcalc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
78 int ipcalc_main(int argc UNUSED_PARAM, char **argv)
79 {
80         unsigned opt;
81         bool have_netmask = 0;
82         struct in_addr s_netmask, s_broadcast, s_network, s_ipaddr;
83         /* struct in_addr { in_addr_t s_addr; }  and  in_addr_t
84          * (which in turn is just a typedef to uint32_t)
85          * are essentially the same type. A few macros for less verbosity: */
86 #define netmask   (s_netmask.s_addr)
87 #define broadcast (s_broadcast.s_addr)
88 #define network   (s_network.s_addr)
89 #define ipaddr    (s_ipaddr.s_addr)
90         char *ipstr;
91
92 #if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
93         applet_long_options = ipcalc_longopts;
94 #endif
95         opt = getopt32(argv, "mbn" IF_FEATURE_IPCALC_FANCY("phs"));
96         argv += optind;
97         if (opt & SILENT)
98                 logmode = LOGMODE_NONE; /* suppress error_msg() output */
99         if (opt & (BROADCAST | NETWORK | NETPREFIX)) {
100                 if (!argv[0] || !argv[1] || argv[2])
101                         bb_show_usage();
102         } else {
103                 if (!argv[0] || argv[1])
104                         bb_show_usage();
105         }
106
107         ipstr = argv[0];
108         if (ENABLE_FEATURE_IPCALC_FANCY) {
109                 unsigned long netprefix = 0;
110                 char *prefixstr;
111
112                 prefixstr = ipstr;
113
114                 while (*prefixstr) {
115                         if (*prefixstr == '/') {
116                                 *prefixstr++ = '\0';
117                                 if (*prefixstr) {
118                                         unsigned msk;
119                                         netprefix = xatoul_range(prefixstr, 0, 32);
120                                         netmask = 0;
121                                         msk = 0x80000000;
122                                         while (netprefix > 0) {
123                                                 netmask |= msk;
124                                                 msk >>= 1;
125                                                 netprefix--;
126                                         }
127                                         netmask = htonl(netmask);
128                                         /* Even if it was 0, we will signify that we have a netmask. This allows */
129                                         /* for specification of default routes, etc which have a 0 netmask/prefix */
130                                         have_netmask = 1;
131                                 }
132                                 break;
133                         }
134                         prefixstr++;
135                 }
136         }
137
138         if (inet_aton(ipstr, &s_ipaddr) == 0) {
139                 bb_error_msg_and_die("bad IP address: %s", argv[0]);
140         }
141
142         if (argv[1]) {
143                 if (ENABLE_FEATURE_IPCALC_FANCY && have_netmask) {
144                         bb_error_msg_and_die("use prefix or netmask, not both");
145                 }
146                 if (inet_aton(argv[1], &s_netmask) == 0) {
147                         bb_error_msg_and_die("bad netmask: %s", argv[1]);
148                 }
149         } else {
150                 /* JHC - If the netmask wasn't provided then calculate it */
151                 if (!ENABLE_FEATURE_IPCALC_FANCY || !have_netmask)
152                         netmask = get_netmask(ipaddr);
153         }
154
155         if (opt & NETMASK) {
156                 printf("NETMASK=%s\n", inet_ntoa(s_netmask));
157         }
158
159         if (opt & BROADCAST) {
160                 broadcast = (ipaddr & netmask) | ~netmask;
161                 printf("BROADCAST=%s\n", inet_ntoa(s_broadcast));
162         }
163
164         if (opt & NETWORK) {
165                 network = ipaddr & netmask;
166                 printf("NETWORK=%s\n", inet_ntoa(s_network));
167         }
168
169         if (ENABLE_FEATURE_IPCALC_FANCY) {
170                 if (opt & NETPREFIX) {
171                         printf("PREFIX=%i\n", get_prefix(netmask));
172                 }
173
174                 if (opt & HOSTNAME) {
175                         struct hostent *hostinfo;
176
177                         hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
178                         if (!hostinfo) {
179                                 bb_herror_msg_and_die("can't find hostname for %s", argv[0]);
180                         }
181                         str_tolower(hostinfo->h_name);
182
183                         printf("HOSTNAME=%s\n", hostinfo->h_name);
184                 }
185         }
186
187         return EXIT_SUCCESS;
188 }