summaryrefslogtreecommitdiffstats
path: root/ipcalc.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipcalc.c')
-rw-r--r--ipcalc.c1990
1 files changed, 1990 insertions, 0 deletions
diff --git a/ipcalc.c b/ipcalc.c
new file mode 100644
index 0000000..b3d2c60
--- /dev/null
+++ b/ipcalc.c
@@ -0,0 +1,1990 @@
+/*
+ * Copyright (c) 1997-2015 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Nikos Mavrogiannopoulos <nmav@redhat.com>
+ * Erik Troan <ewt@redhat.com>
+ * Preston Brown <pbrown@redhat.com>
+ * David Cantrell <dcantrell@redhat.com>
+ */
+
+#define _GNU_SOURCE /* asprintf */
+#include <ctype.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h> /* open */
+#include <fcntl.h> /* open */
+#include <unistd.h> /* read */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <time.h> /* clock_gettime */
+#include "ipcalc.h"
+
+int beSilent = 0;
+static unsigned colors = 0;
+static unsigned flags = 0;
+
+/*!
+ \file ipcalc.c
+ \brief provides utilities for manipulating IP addresses.
+
+ ipcalc provides utilities and a front-end command line interface for
+ manipulating IP addresses, and calculating various aspects of an ip
+ address/netmask/network address/prefix/etc.
+
+ Functionality can be accessed from other languages from the library
+ interface, documented here. To use ipcalc from the shell, read the
+ ipcalc(1) manual page.
+
+ When passing parameters to the various functions, take note of whether they
+ take host byte order or network byte order. Most take host byte order, and
+ return host byte order, but there are some exceptions.
+*/
+
+/*!
+ \fn uint32_t prefix2mask(int bits)
+ \brief creates a netmask from a specified number of bits
+
+ This function converts a prefix length to a netmask. As CIDR (classless
+ internet domain internet domain routing) has taken off, more an more IP
+ addresses are being specified in the format address/prefix
+ (i.e. 192.168.2.3/24, with a corresponding netmask 255.255.255.0). If you
+ need to see what netmask corresponds to the prefix part of the address, this
+ is the function. See also \ref mask2prefix.
+
+ \param prefix is the number of bits to create a mask for.
+ \return a network mask, in network byte order.
+*/
+uint32_t prefix2mask(int prefix)
+{
+ struct in_addr mask;
+ memset(&mask, 0, sizeof(mask));
+ if (prefix) {
+ return htonl(~((1 << (32 - prefix)) - 1));
+ } else {
+ return htonl(0);
+ }
+}
+
+/*!
+ \fn struct in_addr calc_broadcast(struct in_addr addr, int prefix)
+
+ \brief calculate broadcast address given an IP address and a prefix length.
+
+ \param addr an IP address in network byte order.
+ \param prefix a prefix length.
+
+ \return the calculated broadcast address for the network, in network byte
+ order.
+*/
+static struct in_addr calc_broadcast(struct in_addr addr, int prefix)
+{
+ struct in_addr mask;
+ struct in_addr broadcast;
+
+ mask.s_addr = prefix2mask(prefix);
+
+ memset(&broadcast, 0, sizeof(broadcast));
+
+ /* Follow RFC3021 and set the limited broadcast address on /31 */
+ if (prefix == 31)
+ broadcast.s_addr = htonl(0xFFFFFFFF);
+ else
+ broadcast.s_addr = (addr.s_addr & mask.s_addr) | ~mask.s_addr;
+
+ return broadcast;
+}
+
+/*!
+ \fn struct in_addr calc_network(struct in_addr addr, int prefix)
+ \brief calculates the network address for a specified address and prefix.
+
+ \param addr an IP address, in network byte order
+ \param prefix the network prefix
+ \return the base address of the network that addr is associated with, in
+ network byte order.
+*/
+struct in_addr calc_network(struct in_addr addr, int prefix)
+{
+ struct in_addr mask;
+ struct in_addr network;
+
+ mask.s_addr = prefix2mask(prefix);
+
+ memset(&network, 0, sizeof(network));
+ network.s_addr = addr.s_addr & mask.s_addr;
+ return network;
+}
+
+/*!
+ \fn const char *get_hostname(int family, void *addr)
+ \brief returns the hostname associated with the specified IP address
+
+ \param family the address family, either AF_INET or AF_INET6.
+ \param addr an IP address to find a hostname for, in network byte order,
+ should either be a pointer to a struct in_addr or a struct in6_addr.
+
+ \return a hostname, or NULL if one cannot be determined. Hostname is stored
+ in an allocated buffer.
+*/
+static char *get_hostname(int family, void *addr)
+{
+ static char hostname[NI_MAXHOST];
+ int ret = -1;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+
+ if (family == AF_INET) {
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_family = AF_INET;
+ memcpy(&addr4.sin_addr, addr, sizeof(struct in_addr));
+ ret = getnameinfo((struct sockaddr*)&addr4, sizeof(addr4), hostname, sizeof(hostname), NULL, 0, 0);
+ } else if (family == AF_INET6) {
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+ memcpy(&addr6.sin6_addr, addr, sizeof(struct in6_addr));
+ ret = getnameinfo((struct sockaddr*)&addr6, sizeof(addr6), hostname, sizeof(hostname), NULL, 0, 0);
+ }
+
+ if (ret != 0)
+ return NULL;
+
+ return safe_strdup(hostname);
+}
+
+/*!
+ \fn const char *get_ip_address(int family, void *addr)
+ \brief returns the IP address associated with the specified hostname
+
+ \param family the requested address family or AF_UNSPEC for any
+ \param host a hostname
+
+ \return an IP address, or NULL if one cannot be determined. The IP is stored
+ in an allocated buffer.
+*/
+static char *get_ip_address(int family, const char *host)
+{
+ struct addrinfo *res, *rp;
+ struct addrinfo hints;
+ int err;
+ static char ipname[64];
+ void *addr;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+
+ err = getaddrinfo(host, NULL, &hints, &res);
+ if (err != 0)
+ return NULL;
+
+ for (rp=res;rp!=NULL;rp=rp->ai_next) {
+ if (rp->ai_family == AF_INET)
+ addr = (&((struct sockaddr_in *)(rp->ai_addr))->sin_addr);
+ else
+ addr = (&((struct sockaddr_in6 *)(rp->ai_addr))->sin6_addr);
+
+ if (inet_ntop(rp->ai_family, addr, ipname, sizeof(ipname)) != NULL) {
+ freeaddrinfo(res);
+ return safe_strdup(ipname);
+ }
+ }
+
+ freeaddrinfo(res);
+ return NULL;
+}
+
+static int bit_count(uint32_t i)
+{
+ int c = 0;
+
+ while (i > 0) {
+ if (i & 1)
+ c++;
+ i >>= 1;
+ }
+
+ return c;
+}
+
+/*!
+ \fn int mask2prefix(struct in_addr mask)
+ \brief calculates the number of bits masked off by a netmask.
+
+ This function calculates the significant bits in an IP address as specified by
+ a netmask. See also \ref prefix2mask.
+
+ \param mask is the netmask, specified as an struct in_addr in network byte order.
+ \return the number of significant bits. */
+static int mask2prefix(struct in_addr mask)
+{
+ int c = 0;
+ unsigned int seen_one = 0;
+ uint32_t i = ntohl(mask.s_addr);
+
+ while (i > 0) {
+ if (i & 1) {
+ seen_one = 1;
+ c++;
+ } else {
+ if (seen_one) {
+ return -1;
+ }
+ }
+ i >>= 1;
+ }
+
+ return c;
+}
+
+static
+int ipv4_mask_to_int(const char *prefix)
+{
+ int ret;
+ struct in_addr in;
+
+ ret = inet_pton(AF_INET, prefix, &in);
+ if (ret == 0)
+ return -1;
+
+ return mask2prefix(in);
+}
+
+/* Returns powers of two in textual format */
+static const char *p2_table(unsigned pow)
+{
+ static const char *pow2[] = {
+ "1",
+ "2",
+ "4",
+ "8",
+ "16",
+ "32",
+ "64",
+ "128",
+ "256",
+ "512",
+ "1024",
+ "2048",
+ "4096",
+ "8192",
+ "16384",
+ "32768",
+ "65536",
+ "131072",
+ "262144",
+ "524288",
+ "1048576",
+ "2097152",
+ "4194304",
+ "8388608",
+ "16777216",
+ "33554432",
+ "67108864",
+ "134217728",
+ "268435456",
+ "536870912",
+ "1073741824",
+ "2147483648",
+ "4294967296",
+ "8589934592",
+ "17179869184",
+ "34359738368",
+ "68719476736",
+ "137438953472",
+ "274877906944",
+ "549755813888",
+ "1099511627776",
+ "2199023255552",
+ "4398046511104",
+ "8796093022208",
+ "17592186044416",
+ "35184372088832",
+ "70368744177664",
+ "140737488355328",
+ "281474976710656",
+ "562949953421312",
+ "1125899906842624",
+ "2251799813685248",
+ "4503599627370496",
+ "9007199254740992",
+ "18014398509481984",
+ "36028797018963968",
+ "72057594037927936",
+ "144115188075855872",
+ "288230376151711744",
+ "576460752303423488",
+ "1152921504606846976",
+ "2305843009213693952",
+ "4611686018427387904",
+ "9223372036854775808",
+ "18446744073709551616",
+ "36893488147419103232",
+ "73786976294838206464",
+ "147573952589676412928",
+ "295147905179352825856",
+ "590295810358705651712",
+ "1180591620717411303424",
+ "2361183241434822606848",
+ "4722366482869645213696",
+ "9444732965739290427392",
+ "18889465931478580854784",
+ "37778931862957161709568",
+ "75557863725914323419136",
+ "151115727451828646838272",
+ "302231454903657293676544",
+ "604462909807314587353088",
+ "1208925819614629174706176",
+ "2417851639229258349412352",
+ "4835703278458516698824704",
+ "9671406556917033397649408",
+ "19342813113834066795298816",
+ "38685626227668133590597632",
+ "77371252455336267181195264",
+ "154742504910672534362390528",
+ "309485009821345068724781056",
+ "618970019642690137449562112",
+ "1237940039285380274899124224",
+ "2475880078570760549798248448",
+ "4951760157141521099596496896",
+ "9903520314283042199192993792",
+ "19807040628566084398385987584",
+ "39614081257132168796771975168",
+ "79228162514264337593543950336",
+ "158456325028528675187087900672",
+ "316912650057057350374175801344",
+ "633825300114114700748351602688",
+ "1267650600228229401496703205376",
+ "2535301200456458802993406410752",
+ "5070602400912917605986812821504",
+ "10141204801825835211973625643008",
+ "20282409603651670423947251286016",
+ "40564819207303340847894502572032",
+ "81129638414606681695789005144064",
+ "162259276829213363391578010288128",
+ "324518553658426726783156020576256",
+ "649037107316853453566312041152512",
+ "1298074214633706907132624082305024",
+ "2596148429267413814265248164610048",
+ "5192296858534827628530496329220096",
+ "10384593717069655257060992658440192",
+ "20769187434139310514121985316880384",
+ "41538374868278621028243970633760768",
+ "83076749736557242056487941267521536",
+ "166153499473114484112975882535043072",
+ "332306998946228968225951765070086144",
+ "664613997892457936451903530140172288",
+ "1329227995784915872903807060280344576",
+ "2658455991569831745807614120560689152",
+ "5316911983139663491615228241121378304",
+ "10633823966279326983230456482242756608",
+ "21267647932558653966460912964485513216",
+ "42535295865117307932921825928971026432",
+ "85070591730234615865843651857942052864",
+ "170141183460469231731687303715884105728",
+ };
+ if (pow <= 127)
+ return pow2[pow];
+ return "";
+}
+
+static const char *ipv4_net_to_type(struct in_addr net)
+{
+ unsigned byte1 = (ntohl(net.s_addr) >> 24) & 0xff;
+ unsigned byte2 = (ntohl(net.s_addr) >> 16) & 0xff;
+ unsigned byte3 = (ntohl(net.s_addr) >> 8) & 0xff;
+ unsigned byte4 = (ntohl(net.s_addr)) & 0xff;
+
+ /* based on IANA's iana-ipv4-special-registry and ipv4-address-space
+ * Updated: 2020-04-06
+ */
+ if (byte1 == 0) {
+ return "This host on this network";
+ }
+
+ if (byte1 == 10) {
+ return "Private Use";
+ }
+
+ if (byte1 == 100 && (byte2 & 0xc0) == 64) {
+ return "Shared Address Space";
+ }
+
+ if (byte1 == 127) {
+ return "Loopback";
+ }
+
+ if (byte1 == 169 && byte2 == 254) {
+ return "Link Local";
+ }
+
+ if (byte1 == 172 && (byte2 & 0xf0) == 16) {
+ return "Private Use";
+ }
+
+ if (byte1 == 192 && byte2 == 0 && byte3 == 0 && byte4 <= 7) {
+ return "IPv4 Service Continuity Prefix";
+ }
+
+ if (byte1 == 192 && byte2 == 0 && byte3 == 0 && byte4 == 8) {
+ return "IPv4 dummy address";
+ }
+
+ if (byte1 == 192 && byte2 == 0 && byte3 == 0 && byte4 == 9) {
+ return "Port Control Protocol Anycast";
+ }
+
+ if (byte1 == 192 && byte2 == 0 && byte3 == 0 && byte4 == 10) {
+ return "Traversal Using Relays around NAT Anycast";
+ }
+
+ if (byte1 == 192 && byte2 == 0 && byte3 == 0 && (byte4 == 170 || byte4 == 171)) {
+ return "NAT64/DNS64 Discovery";
+ }
+
+ if (byte1 == 192 && byte2 == 0 && byte3 == 0) {
+ return "IETF Protocol Assignments";
+ }
+
+ if (byte1 == 192 && byte2 == 0 && byte3 == 2) {
+ return "Documentation (TEST-NET-1)";
+ }
+
+ if (byte1 == 198 && byte2 == 51 && byte3 == 100) {
+ return "Documentation (TEST-NET-2)";
+ }
+
+ if (byte1 == 203 && byte2 == 0 && byte3 == 113) {
+ return "Documentation (TEST-NET-3)";
+ }
+
+ if (byte1 == 192 && byte2 == 88 && byte3 == 99) {
+ return "6 to 4 Relay Anycast (Deprecated)";
+ }
+
+ if (byte1 == 192 && byte2 == 31 && byte3 == 196) {
+ return "AS112-v4";
+ }
+
+ if (byte1 == 192 && byte2 == 52 && byte3 == 193) {
+ return "AMT";
+ }
+
+ if (byte1 == 192 && byte2 == 168) {
+ return "Private Use";
+ }
+
+ if (byte1 == 192 && byte2 == 175 && byte3 == 48) {
+ return "Direct Delegation AS112 Service";
+ }
+
+ if (byte1 == 255 && byte2 == 255 && byte3 == 255 && byte4 == 255) {
+ return "Limited Broadcast";
+ }
+
+ if (byte1 == 198 && (byte2 & 0xfe) == 18) {
+ return "Benchmarking";
+ }
+
+ if (byte1 >= 224 && byte1 <= 239) {
+ return "Multicast";
+ }
+
+ if ((byte1 & 0xf0) == 240) {
+ return "Reserved";
+ }
+
+ return "Internet";
+}
+
+static
+const char *ipv4_net_to_class(struct in_addr net)
+{
+ unsigned byte1 = (ntohl(net.s_addr) >> 24) & 0xff;
+
+ if (byte1 < 128) {
+ return "Class A";
+ }
+
+ if (byte1 >= 128 && byte1 < 192) {
+ return "Class B";
+ }
+
+ if (byte1 >= 192 && byte1 < 224) {
+ return "Class C";
+ }
+
+ if (byte1 >= 224 && byte1 < 239) {
+ return "Class D";
+ }
+
+ return "Class E";
+}
+
+static
+unsigned default_ipv4_prefix(struct in_addr net)
+{
+ unsigned byte1 = (ntohl(net.s_addr) >> 24) & 0xff;
+
+ if (byte1 < 128) {
+ return 8;
+ }
+
+ if (byte1 >= 128 && byte1 < 192) {
+ return 16;
+ }
+
+ if (byte1 >= 192 && byte1 < 224) {
+ return 24;
+ }
+
+ return 24;
+}
+
+char *ipv4_prefix_to_hosts(char *hosts, unsigned hosts_size, unsigned prefix)
+{
+ if (prefix >= 31) {
+ snprintf(hosts, hosts_size, "%s", p2_table(32 - prefix));
+ } else {
+ unsigned tmp = (1 << (32 - prefix)) - 2;
+ snprintf(hosts, hosts_size, "%u", tmp);
+ }
+ return hosts;
+}
+
+char *ipv6_prefix_to_hosts(char *hosts, unsigned hosts_size, unsigned prefix)
+{
+ snprintf(hosts, hosts_size, "%s", p2_table(128 - prefix));
+ return hosts;
+}
+
+
+static
+int get_ipv4_info(const char *ipStr, int prefix, ip_info_st * info,
+ unsigned flags)
+{
+ struct in_addr ip, netmask, network, broadcast, minhost, maxhost;
+ char namebuf[INET_ADDRSTRLEN + 1];
+ char errBuf[250];
+
+ memset(info, 0, sizeof(*info));
+
+ if (inet_pton(AF_INET, ipStr, &ip) <= 0) {
+ if (!beSilent)
+ fprintf(stderr, "ipcalc: bad IPv4 address: %s\n",
+ ipStr);
+ return -1;
+ }
+
+ /* Handle CIDR entries such as 172/8 */
+ if (prefix >= 0) {
+ char *tmp = (char *)ipStr;
+ int i;
+
+ for (i = 3; i > 0; i--) {
+ tmp = strchr(tmp, '.');
+ if (!tmp)
+ break;
+ else
+ tmp++;
+ }
+
+ tmp = NULL;
+ for (; i > 0; i--) {
+ if (asprintf(&tmp, "%s.0", ipStr) == -1) {
+ fprintf(stderr,
+ "Memory allocation failure line %d\n",
+ __LINE__);
+ abort();
+ }
+ ipStr = tmp;
+ }
+ } else { /* assume good old days classful Internet */
+ if (flags & FLAG_ASSUME_CLASS_PREFIX)
+ prefix = default_ipv4_prefix(ip);
+ else
+ prefix = 32;
+ }
+
+ if (prefix > 32) {
+ if (!beSilent)
+ fprintf(stderr, "ipcalc: bad IPv4 prefix %d\n", prefix);
+ return -1;
+ }
+
+ if (inet_ntop(AF_INET, &ip, namebuf, sizeof(namebuf)) == 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: error calculating the IPv4 network\n");
+ return -1;
+ }
+ info->ip = safe_strdup(namebuf);
+
+ netmask.s_addr = prefix2mask(prefix);
+ memset(namebuf, '\0', sizeof(namebuf));
+
+ if (inet_ntop(AF_INET, &netmask, namebuf, INET_ADDRSTRLEN) == NULL) {
+ fprintf(stderr, "inet_ntop failure at line %d\n",
+ __LINE__);
+ exit(1);
+ }
+ info->netmask = safe_strdup(namebuf);
+ info->prefix = prefix;
+
+ broadcast = calc_broadcast(ip, prefix);
+
+ memset(namebuf, '\0', sizeof(namebuf));
+ if (inet_ntop(AF_INET, &broadcast, namebuf, INET_ADDRSTRLEN) == NULL) {
+ fprintf(stderr, "inet_ntop failure at line %d\n",
+ __LINE__);
+ exit(1);
+ }
+ info->broadcast = safe_strdup(namebuf);
+
+ network = calc_network(ip, prefix);
+
+ info->reverse_dns = calc_reverse_dns4(network, prefix, network, broadcast);
+
+ memset(namebuf, '\0', sizeof(namebuf));
+ if (inet_ntop(AF_INET, &network, namebuf, INET_ADDRSTRLEN) == NULL) {
+ fprintf(stderr, "inet_ntop failure at line %d\n",
+ __LINE__);
+ exit(1);
+ }
+
+ info->network = safe_strdup(namebuf);
+
+ info->type = ipv4_net_to_type(network);
+ info->class = ipv4_net_to_class(network);
+
+ if (prefix < 32) {
+ memcpy(&minhost, &network, sizeof(minhost));
+
+ if (prefix <= 30)
+ minhost.s_addr = htonl(ntohl(minhost.s_addr) | 1);
+ if (inet_ntop(AF_INET, &minhost, namebuf, INET_ADDRSTRLEN) ==
+ NULL) {
+ fprintf(stderr, "inet_ntop failure at line %d\n",
+ __LINE__);
+ exit(1);
+ }
+ info->hostmin = safe_strdup(namebuf);
+
+ memcpy(&maxhost, &network, sizeof(minhost));
+ maxhost.s_addr |= ~netmask.s_addr;
+ if (prefix <= 30) {
+ maxhost.s_addr = htonl(ntohl(maxhost.s_addr) - 1);
+ }
+ if (inet_ntop(AF_INET, &maxhost, namebuf, sizeof(namebuf)) == 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: error calculating the IPv4 network\n");
+ return -1;
+ }
+
+ info->hostmax = safe_strdup(namebuf);
+ } else {
+ info->hostmin = info->network;
+ info->hostmax = info->network;
+ }
+
+ ipv4_prefix_to_hosts(info->hosts, sizeof(info->hosts), prefix);
+
+#if defined(USE_GEOIP) || defined(USE_MAXMIND)
+ if (flags & FLAG_GET_GEOIP) {
+ geo_ip_lookup(ipStr, &info->geoip_country, &info->geoip_ccode, &info->geoip_city, &info->geoip_coord);
+ }
+#endif
+
+ if (flags & FLAG_RESOLVE_HOST) {
+ info->hostname = get_hostname(AF_INET, &ip);
+ if (info->hostname == NULL) {
+ if (!beSilent) {
+ sprintf(errBuf,
+ "ipcalc: cannot find hostname for %s",
+ ipStr);
+ herror(errBuf);
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int ipv6_prefix_to_mask(unsigned prefix, struct in6_addr *mask)
+{
+ struct in6_addr in6;
+ int i, j;
+
+ if (prefix > 128)
+ return -1;
+
+ memset(&in6, 0x0, sizeof(in6));
+ for (i = prefix, j = 0; i > 0; i -= 8, j++) {
+ if (i >= 8) {
+ in6.s6_addr[j] = 0xff;
+ } else {
+ in6.s6_addr[j] = (unsigned long)(0xffU << (8 - i));
+ }
+ }
+
+ memcpy(mask, &in6, sizeof(*mask));
+ return 0;
+}
+
+static char *ipv6_mask_to_str(const struct in6_addr *mask)
+{
+ char buf[128];
+
+ if (inet_ntop(AF_INET6, mask, buf, sizeof(buf)) == NULL)
+ return NULL;
+
+ return safe_strdup(buf);
+}
+
+static const char *ipv6_net_to_type(struct in6_addr *net, int prefix)
+{
+ uint16_t word1 = net->s6_addr[0] << 8 | net->s6_addr[1];
+ uint16_t word2 = net->s6_addr[2] << 8 | net->s6_addr[3];
+
+ /* based on IANA's iana-ipv6-special-registry and ipv6-address-space
+ * Updated: 2019-09-13
+ */
+ if (prefix == 128 && memcmp
+ (net->s6_addr,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+ 16) == 0)
+ return "Loopback Address";
+
+ if (prefix == 128 && memcmp
+ (net->s6_addr,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ 16) == 0)
+ return "Unspecified Address";
+
+ if (prefix >= 96 && memcmp
+ (net->s6_addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff",
+ 12) == 0)
+ return "IPv4-mapped Address";
+
+ if (prefix >= 96 && memcmp
+ (net->s6_addr, "\x00\x64\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00",
+ 12) == 0)
+ return "IPv4-IPv6 Translat.";
+
+ if (prefix >= 48 && memcmp
+ (net->s6_addr, "\x00\x64\xff\x9b\x00\x01",
+ 6) == 0)
+ return "IPv4-IPv6 Translat.";
+
+ if (prefix >= 96 && memcmp
+ (net->s6_addr, "\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ 12) == 0)
+ return "Discard-Only Address Block";
+
+ if (prefix >= 32 && word1 == 0x2001 && word2 == 0)
+ return "TEREDO";
+
+ if (prefix == 128 && memcmp
+ (net->s6_addr, "\x20\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
+ 16) == 0)
+ return "Port Control Protocol Anycast";
+
+ if (prefix == 128 && memcmp
+ (net->s6_addr, "\x20\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02",
+ 16) == 0)
+ return "Traversal Using Relays around NAT Anycast";
+
+ if (prefix >= 48 && memcmp
+ (net->s6_addr, "\x20\x01\x00\x02\x00\x00",
+ 6) == 0)
+ return "Benchmarking";
+
+ if (prefix >= 32 && word1 == 0x2001 && word2 == 0x3)
+ return "AMT";
+
+ if (prefix >= 48 && memcmp
+ (net->s6_addr, "\x20\x01\x00\x04\x01\x12",
+ 6) == 0)
+ return "AS112-v6";
+
+ if (prefix >= 28 && word1 == 0x2001 && (word2 & 0xfff0) == 0x10)
+ return "Deprecated (previously ORCHID)";
+
+ if (prefix >= 28 && word1 == 0x2001 && (word2 & 0xfff0) == 0x20)
+ return "ORCHIDv2";
+
+ if (prefix >= 23 && word1 == 0x2001 && (word2 & 0xff00) <= 0x100)
+ return "IETF Protocol Assignments";
+
+ if (prefix >= 32 && word1 == 0x2001 && word2 == 0xdb8)
+ return "Documentation";
+
+ if (word1 == 0x2002)
+ return "6to4";
+
+ if (prefix >= 48 && memcmp
+ (net->s6_addr, "\x26\x20\x00\x4f\x80\x00",
+ 6) == 0)
+ return "Direct Delegation AS112 Service";
+
+ if ((word1 & 0xe000) == 0x2000) {
+ return "Global Unicast";
+ }
+
+ if (((net->s6_addr[0] & 0xfe) == 0xfc)) {
+ return "Unique Local Unicast";
+ }
+
+ if ((word1 & 0xffc0) == 0xfe80) {
+ return "Link-Scoped Unicast";
+ }
+
+ if ((net->s6_addr[0] & 0xff) == 0xff) {
+ return "Multicast";
+ }
+
+ return "Reserved";
+}
+
+static
+char *expand_ipv6(struct in6_addr *ip6)
+{
+ char buf[128];
+ char *p;
+ unsigned i;
+
+ p = buf;
+ for (i = 0; i < 16; i++) {
+ sprintf(p, "%.2x", (unsigned)ip6->s6_addr[i]);
+ p += 2;
+ if (i % 2 != 0 && i != 15) {
+ *p = ':';
+ p++;
+ }
+ }
+ *p = 0;
+
+ return safe_strdup(buf);
+}
+
+static
+int get_ipv6_info(const char *ipStr, int prefix, ip_info_st * info,
+ unsigned flags)
+{
+ struct in6_addr ip6, mask, network;
+ char errBuf[250];
+ unsigned i;
+
+ memset(info, 0, sizeof(*info));
+
+ if (inet_pton(AF_INET6, ipStr, &ip6) <= 0) {
+ if (!beSilent)
+ fprintf(stderr, "ipcalc: bad IPv6 address: %s\n",
+ ipStr);
+ return -1;
+ }
+
+ /* expand */
+ info->expanded_ip = expand_ipv6(&ip6);
+
+ if (inet_ntop(AF_INET6, &ip6, errBuf, sizeof(errBuf)) == 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: error calculating the IPv6 network\n");
+ return -1;
+ }
+
+ info->ip = safe_strdup(errBuf);
+
+ if (prefix > 128) {
+ if (!beSilent)
+ fprintf(stderr, "ipcalc: bad IPv6 prefix: %d\n",
+ prefix);
+ return -1;
+ } else if (prefix < 0) {
+ prefix = 128;
+ }
+
+ info->prefix = prefix;
+
+ if (ipv6_prefix_to_mask(prefix, &mask) == -1) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: error converting IPv6 prefix: %d\n",
+ prefix);
+ return -1;
+ }
+
+ info->netmask = ipv6_mask_to_str(&mask);
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ network.s6_addr[i] = ip6.s6_addr[i] & mask.s6_addr[i];
+
+ if (inet_ntop(AF_INET6, &network, errBuf, sizeof(errBuf)) == 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: error calculating the IPv6 network\n");
+ return -1;
+ }
+
+ info->network = safe_strdup(errBuf);
+
+ info->expanded_network = expand_ipv6(&network);
+ info->type = ipv6_net_to_type(&network, prefix);
+
+ info->reverse_dns = calc_reverse_dns6(&network, prefix);
+
+ if (prefix < 128) {
+ info->hostmin = safe_strdup(errBuf);
+
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ network.s6_addr[i] |= ~mask.s6_addr[i];
+ if (inet_ntop(AF_INET6, &network, errBuf, sizeof(errBuf)) == 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: error calculating the IPv6 network\n");
+ return -1;
+ }
+
+ info->hostmax = safe_strdup(errBuf);
+ } else {
+ info->hostmin = info->network;
+ info->hostmax = info->network;
+ }
+
+ ipv6_prefix_to_hosts(info->hosts, sizeof(info->hosts), prefix);
+
+#if defined(USE_GEOIP) || defined(USE_MAXMIND)
+ if (flags & FLAG_GET_GEOIP) {
+ geo_ip_lookup(ipStr, &info->geoip_country, &info->geoip_ccode, &info->geoip_city, &info->geoip_coord);
+ }
+#endif
+
+ if (flags & FLAG_RESOLVE_HOST) {
+ info->hostname = get_hostname(AF_INET6, &ip6);
+ if (info->hostname == NULL) {
+ if (!beSilent) {
+ sprintf(errBuf,
+ "ipcalc: cannot find hostname for %s",
+ ipStr);
+ herror(errBuf);
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int randomize(void *ptr, unsigned size)
+{
+ int fd, ret;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ ret = read(fd, ptr, size);
+ close(fd);
+
+ if (ret != size) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *generate_ip_network(unsigned prefix, unsigned flags)
+{
+ struct timespec ts;
+ char ipbuf[64];
+ char *p = NULL;
+
+ if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) < 0)
+ return NULL;
+
+ if (flags & FLAG_IPV6) {
+ struct in6_addr net;
+
+ net.s6_addr[0] = 0xfd;
+ if (randomize(&net.s6_addr[1], 15) < 0)
+ return NULL;
+
+ if (inet_ntop(AF_INET6, &net, ipbuf, sizeof(ipbuf)) == NULL)
+ return NULL;
+ } else {
+ struct in_addr net;
+ unsigned c = ts.tv_nsec % 4;
+ uint8_t bytes[4];
+
+ if (randomize(bytes, 4) < 0)
+ return NULL;
+
+ if (prefix >= 16 && c < 2) {
+ if (c == 1) {
+ bytes[0] = 192;
+ bytes[1] = 168;
+ } else {
+ bytes[0] = 172;
+ bytes[1] = 16 | ((ts.tv_nsec >> 4) & 0x0f);
+ }
+ } else {
+ bytes[0] = 10;
+ }
+
+ memcpy(&net.s_addr, bytes, 4);
+
+ if (inet_ntop(AF_INET, &net, ipbuf, sizeof(ipbuf)) == NULL)
+ return NULL;
+ }
+
+ if (asprintf(&p, "%s/%u", ipbuf, prefix) == -1)
+ return NULL;
+
+ return p;
+}
+
+static
+int str_to_prefix(unsigned *flags, const char *prefixStr, unsigned fix)
+{
+ int prefix = -1;
+ if (!((*flags) & FLAG_IPV6) && strchr(prefixStr, '.')) { /* prefix is 255.x.x.x */
+ prefix = ipv4_mask_to_int(prefixStr);
+ } else {
+ int r = safe_atoi(prefixStr, &prefix);
+ if (r != 0) {
+ return -1;
+ }
+ }
+
+ if (fix && (prefix > 32 && !((*flags) & FLAG_IPV6)))
+ *flags |= FLAG_IPV6;
+
+ if (prefix < 0 || ((((*flags) & FLAG_IPV6) && prefix > 128) || (!((*flags) & FLAG_IPV6) && prefix > 32))) {
+ return -1;
+ }
+ return prefix;
+}
+
+#define OPT_ALLINFO 1
+#define OPT_MINADDR 2
+#define OPT_MAXADDR 3
+#define OPT_ADDRESSES 4
+#define OPT_ADDRSPACE 5
+#define OPT_USAGE 6
+#define OPT_REVERSE 7
+#define OPT_CLASS_PREFIX 8
+#define OPT_NO_DECORATE 9
+
+static const struct option long_options[] = {
+ {"check", 0, 0, 'c'},
+ {"random-private", 1, 0, 'r'},
+ {"split", 1, 0, 'S'},
+ {"deaggregate", 1, 0, 'd'},
+ {"info", 0, 0, 'i'},
+ {"all-info", 0, 0, OPT_ALLINFO},
+ {"ipv4", 0, 0, '4'},
+ {"ipv6", 0, 0, '6'},
+ {"address", 0, 0, 'a'},
+ {"broadcast", 0, 0, 'b'},
+ {"hostname", 0, 0, 'h'},
+ {"lookup-host", 1, 0, 'o'},
+ {"reverse-dns", 0, 0, OPT_REVERSE},
+#if defined(USE_GEOIP) || defined(USE_MAXMIND)
+ {"geoinfo", 0, 0, 'g'},
+#endif
+ {"netmask", 0, 0, 'm'},
+ {"network", 0, 0, 'n'},
+ {"prefix", 0, 0, 'p'},
+ {"class-prefix", 0, 0, OPT_CLASS_PREFIX},
+ {"minaddr", 0, 0, OPT_MINADDR},
+ {"maxaddr", 0, 0, OPT_MAXADDR},
+ {"addresses", 0, 0, OPT_ADDRESSES},
+ {"addrspace", 0, 0, OPT_ADDRSPACE},
+ {"silent", 0, 0, 's'},
+ {"no-decorate", 0, 0, OPT_NO_DECORATE},
+ {"json", 0, 0, 'j'},
+ {"version", 0, 0, 'v'},
+ {"help", 0, 0, '?'},
+ {"usage", 0, 0, OPT_USAGE},
+ {NULL, 0, 0, 0}
+};
+
+static
+void usage(unsigned verbose)
+{
+ if (verbose) {
+ fprintf(stderr, "Usage: ipcalc [OPTION...]\n");
+ fprintf(stderr, " -c, --check Validate IP address\n");
+ fprintf(stderr, " -r, --random-private=PREFIX Generate a random private IP network using\n");
+ fprintf(stderr, " the supplied prefix or mask.\n");
+ fprintf(stderr, " -S, --split=PREFIX Split the provided network using the\n");
+ fprintf(stderr, " provided prefix/netmask\n");
+ fprintf(stderr, " -d, --deaggregate=IP1-IP2 Deaggregate the provided address range\n");
+ fprintf(stderr, " -i, --info Print information on the provided IP address\n");
+ fprintf(stderr, " (default)\n");
+ fprintf(stderr, " --all-info Print verbose information on the provided IP\n");
+ fprintf(stderr, " address\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Specific info options:\n");
+ fprintf(stderr, " --reverse-dns Print network in a the reverse DNS format\n");
+ fprintf(stderr, " -a, --address Display IP address\n");
+ fprintf(stderr, " -b, --broadcast Display calculated broadcast address\n");
+ fprintf(stderr, " -m, --netmask Display netmask for IP\n");
+ fprintf(stderr, " -n, --network Display network address\n");
+ fprintf(stderr, " -p, --prefix Display network prefix\n");
+ fprintf(stderr, " --minaddr Display the minimum address in the network\n");
+ fprintf(stderr, " --maxaddr Display the maximum address in the network\n");
+ fprintf(stderr, " --addresses Display the maximum number of addresses in\n");
+ fprintf(stderr, " the network\n");
+ fprintf(stderr, " --addrspace Display the address space the network\n");
+ fprintf(stderr, " resides on\n");
+ fprintf(stderr, " -h, --hostname Show hostname determined via DNS\n");
+ fprintf(stderr, " -o, --lookup-host=STRING Show IP as determined via DNS\n");
+#if defined(USE_GEOIP) || defined(USE_MAXMIND)
+ fprintf(stderr, " -g, --geoinfo Show Geographic information about the\n");
+ fprintf(stderr, " provided IP\n");
+#endif
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Other options:\n");
+ fprintf(stderr, " -4, --ipv4 Explicitly specify the IPv4 address family\n");
+ fprintf(stderr, " -6, --ipv6 Explicitly specify the IPv6 address family\n");
+ fprintf(stderr, " --class-prefix When specified the default prefix will be determined\n");
+ fprintf(stderr, " by the IPv4 address class\n");
+ fprintf(stderr, " --no-decorate Print only the requested information\n");
+ fprintf(stderr, " -j, --json JSON output\n");
+ fprintf(stderr, " -s, --silent Don't ever display error messages\n");
+ fprintf(stderr, " -v, --version Display program version\n");
+ fprintf(stderr, " -?, --help Show this help message\n");
+ fprintf(stderr, " --usage Display brief usage message\n");
+ } else {
+ fprintf(stderr, "Usage: ipcalc [-46sv?] [-c|--check] [-r|--random-private=STRING] [-i|--info]\n");
+ fprintf(stderr, " [--all-info] [-4|--ipv4] [-6|--ipv6] [-a|--address] [-b|--broadcast]\n");
+ fprintf(stderr, " [-h|--hostname] [-o|--lookup-host=STRING] [-g|--geoinfo]\n");
+ fprintf(stderr, " [-m|--netmask] [-n|--network] [-p|--prefix] [--minaddr] [--maxaddr]\n");
+ fprintf(stderr, " [--addresses] [--addrspace] [-j|--json] [-s|--silent] [-v|--version]\n");
+ fprintf(stderr, " [--reverse-dns] [--class-prefix]\n");
+ fprintf(stderr, " [-?|--help] [--usage]\n");
+ }
+}
+
+void output_start(unsigned * const jsonfirst)
+{
+ if (flags & FLAG_JSON) {
+ printf("{\n");
+ }
+
+ *jsonfirst = JSON_FIRST;
+}
+
+void output_separate(unsigned * const jsonfirst)
+{
+ if (!(flags & FLAG_JSON)) {
+ printf("\n");
+ }
+}
+
+void output_stop(unsigned * const jsonfirst)
+{
+ if (flags & FLAG_JSON) {
+ printf("\n}\n");
+ }
+}
+
+void array_start(unsigned * const jsonfirst, const char *head, const char *json_head)
+{
+ if (flags & FLAG_JSON) {
+ if (*jsonfirst == JSON_NEXT) {
+ printf(",\n ");
+ }
+
+ printf(" \"%s\":[\n ", json_head);
+ } else {
+ if (!(flags & FLAG_NO_DECORATE))
+ printf("[%s]\n", head);
+ }
+
+ *jsonfirst = JSON_ARRAY_FIRST;
+}
+
+void array_stop(unsigned * const jsonfirst)
+{
+ if (flags & FLAG_JSON) {
+ printf("]");
+ *jsonfirst = JSON_NEXT;
+ }
+}
+
+void
+__attribute__ ((format(printf, 3, 4)))
+color_printf(const char *color, const char *title, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ va_color_printf(color, title, fmt, args);
+ va_end(args);
+
+ return;
+}
+
+void va_color_printf(const char *color, const char *title, const char *fmt, va_list varglist)
+{
+ int ret;
+ char *str = NULL;
+
+ ret = vasprintf(&str, fmt, varglist);
+
+ if (ret < 0) {
+ return;
+ }
+
+ fputs(title, stdout);
+ if (colors) {
+ fputs(color, stdout);
+ }
+ fputs(str, stdout);
+ fputs("\n", stdout);
+ if (colors) {
+ fputs(KRESET, stdout);
+ }
+ free(str);
+ return;
+}
+
+void
+__attribute__ ((format(printf, 3, 4)))
+json_printf(unsigned * const jsonfirst, const char *jsontitle, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ va_json_printf(jsonfirst, jsontitle, fmt, args);
+ va_end(args);
+
+ return;
+}
+void va_json_printf(unsigned * const jsonfirst, const char *jsontitle, const char *fmt, va_list varglist)
+{
+ int ret;
+ char *str = NULL;
+
+ ret = vasprintf(&str, fmt, varglist);
+ if (ret < 0)
+ return;
+
+ if (*jsonfirst == JSON_ARRAY_NEXT) {
+ fprintf(stdout, ",\n ");
+ } else if (*jsonfirst == JSON_NEXT) {
+ fprintf(stdout, ",\n");
+ }
+
+ fprintf(stdout, " ");
+ if (jsontitle)
+ fprintf(stdout, "\"%s\":\"%s\"", jsontitle, str);
+ else
+ fprintf(stdout, "\"%s\"", str);
+ if (*jsonfirst == JSON_FIRST)
+ *jsonfirst = JSON_NEXT;
+ else if (*jsonfirst == JSON_ARRAY_FIRST)
+ *jsonfirst = JSON_ARRAY_NEXT;
+
+ free(str);
+ return;
+}
+
+/* Always prints "title: value", will not color if --no-decorate is given
+ */
+static void
+__attribute__ ((format(printf, 4, 5)))
+pretty_printf(unsigned * const jsonfirst, const char *title, const char *jsontitle, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (flags & FLAG_JSON) {
+ va_json_printf(jsonfirst, jsontitle, fmt, args);
+ } else if (flags & FLAG_NO_DECORATE) {
+ fputs(title, stdout);
+ vprintf(fmt, args);
+ fputs("\n", stdout);
+ } else {
+ va_color_printf(KBLUE, title, fmt, args);
+ }
+ va_end(args);
+
+ return;
+}
+
+/* Always prints "title: value", will not color if --no-decorate is given
+ * To be used for distinct values (e.g., a summary).
+ */
+static void
+__attribute__ ((format(printf, 4, 5)))
+pretty_dist_printf(unsigned * const jsonfirst, const char *title, const char *jsontitle, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (flags & FLAG_JSON) {
+ va_json_printf(jsonfirst, jsontitle, fmt, args);
+ } else if (flags & FLAG_NO_DECORATE) {
+ fputs(title, stdout);
+ vprintf(fmt, args);
+ fputs("\n", stdout);
+ } else {
+ va_color_printf(KMAG, title, fmt, args);
+ }
+ va_end(args);
+
+ return;
+}
+
+/* Prints "title: value", will only print value if --no-decorate is given
+ */
+void
+__attribute__ ((format(printf, 4, 5)))
+default_printf(unsigned * const jsonfirst, const char *title, const char *jsontitle, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (flags & FLAG_JSON) {
+ va_json_printf(jsonfirst, jsontitle, fmt, args);
+ } else if (flags & FLAG_NO_DECORATE) {
+ vprintf(fmt, args);
+ printf("\n");
+ } else {
+ va_color_printf(KBLUE, title, fmt, args);
+ }
+ va_end(args);
+
+ return;
+}
+
+/* Prints "title: value", will only print value if --no-decorate is given. It
+ * prints a distinct value (e.g., to be used for a summary).
+ */
+void
+__attribute__ ((format(printf, 4, 5)))
+dist_printf(unsigned * const jsonfirst, const char *title, const char *jsontitle, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (flags & FLAG_JSON) {
+ va_json_printf(jsonfirst, jsontitle, fmt, args);
+ }
+ else if (flags & FLAG_NO_DECORATE) {
+ vprintf(fmt, args);
+ printf("\n");
+ } else {
+ va_color_printf(KMAG, title, fmt, args);
+ }
+ va_end(args);
+
+ return;
+}
+
+/* Printed variables names */
+#define HOSTNAME_NAME "HOSTNAME"
+#define FULL_ADDRESS_NAME "FULLADDRESS"
+#define ADDRESS_NAME "ADDRESS"
+#define FULL_NETWORK_NAME "FULLNETWORK"
+#define NETWORK_NAME "NETWORK"
+#define NETMASK_NAME "NETMASK"
+#define PREFIX_NAME "PREFIX"
+#define BROADCAST_NAME "BROADCAST"
+#define REVERSEDNS_NAME "REVERSEDNS"
+#define ADDRSPACE_NAME "ADDRSPACE"
+#define ADDRCLASS_NAME "ADDRCLASS"
+#define MINADDR_NAME "MINADDR"
+#define MAXADDR_NAME "MAXADDR"
+#define ADDRESSES_NAME "ADDRESSES"
+#define COUNTRYCODE_NAME "COUNTRYCODE"
+#define COUNTRY_NAME "COUNTRY"
+#define CITY_NAME "CITY"
+#define COORDINATES_NAME "COORDINATES"
+
+/*!
+ \fn main(int argc, const char **argv)
+ \brief wrapper program for ipcalc functions.
+
+ This is a wrapper program for the functions that the ipcalc library provides.
+ It can be used from shell scripts or directly from the command line.
+
+ For more information, please see the ipcalc(1) man page.
+*/
+int main(int argc, char **argv)
+{
+ char *randomStr = NULL;
+ char *hostname = NULL;
+ char *splitStr = NULL;
+ char *ipStr = NULL, *prefixStr = NULL, *netmaskStr = NULL, *chptr = NULL;
+ int prefix = -1, splitPrefix = -1;
+ ip_info_st info;
+ int r = 0;
+ unsigned jsonchain = JSON_FIRST;
+ enum app_t app = 0;
+
+ while (1) {
+ int c = getopt_long(argc, argv, "S:cr:i46abho:gmnpjsvd:", long_options, NULL);
+ if (c == -1)
+ break;
+
+ switch(c) {
+ case 'c':
+ app |= APP_CHECK_ADDRESS;
+ break;
+ case 'S':
+ app |= APP_SPLIT;
+ splitStr = safe_strdup(optarg);
+ if (splitStr == NULL) exit(1);
+ break;
+ case 'd':
+ app |= APP_DEAGGREGATE;
+ ipStr = safe_strdup(optarg);
+ if (ipStr == NULL) exit(1);
+ break;
+ case 'r':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_RANDOM;
+ randomStr = safe_strdup(optarg);
+ if (randomStr == NULL) exit(1);
+ break;
+ case 'i':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_MODERN_INFO;
+ break;
+ case OPT_ALLINFO:
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_ALL_INFO|FLAG_SHOW_MODERN_INFO;
+ break;
+ case OPT_CLASS_PREFIX:
+ flags |= FLAG_ASSUME_CLASS_PREFIX;
+ break;
+ case OPT_REVERSE:
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_REVERSE;
+ break;
+ case '4':
+ flags |= FLAG_IPV4;
+ break;
+ case '6':
+ flags |= FLAG_IPV6;
+ break;
+ case 'a':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_ADDRESS;
+ break;
+ case 'b':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_BROADCAST;
+ break;
+ case 'h':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_RESOLVE_HOST;
+ break;
+ case 'o':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_RESOLVE_IP;
+ hostname = safe_strdup(optarg);
+ if (hostname == NULL) exit(1);
+ break;
+ case 'g':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_GEOIP;
+ break;
+ case 'm':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_NETMASK;
+ break;
+ case 'n':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_NETWORK;
+ break;
+ case 'p':
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_PREFIX;
+ break;
+ case OPT_MINADDR:
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_MINADDR;
+ break;
+ case OPT_MAXADDR:
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_MAXADDR;
+ break;
+ case OPT_ADDRESSES:
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_ADDRESSES;
+ break;
+ case OPT_ADDRSPACE:
+ app |= APP_SHOW_INFO;
+ flags |= FLAG_SHOW_ADDRSPACE;
+ break;
+ case OPT_NO_DECORATE:
+ flags |= FLAG_NO_DECORATE;
+ break;
+ case 'j':
+ flags |= FLAG_JSON;
+ break;
+ case 's':
+ beSilent = 1;
+ break;
+ case 'v':
+ app |= APP_VERSION;
+ break;
+ case OPT_USAGE:
+ usage(0);
+ exit(0);
+ case '?':
+ usage(1);
+ exit(0);
+ }
+ }
+
+ if (optind < argc) {
+ if (ipStr != NULL) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: superfluous option given\n");
+ exit(1);
+ }
+
+ ipStr = argv[optind++];
+ if (optind < argc)
+ chptr = argv[optind++];
+ }
+
+ if ((flags & FLAG_JSON) && (flags & FLAG_NO_DECORATE)) {
+ flags &= ~FLAG_NO_DECORATE;
+ }
+
+ if (!(flags & FLAGS_TO_IGNORE_MASK))
+ flags |= FLAG_SHOW_MODERN_INFO;
+
+ /* Only the modern info flag results to JSON output */
+ if ((flags & ENV_INFO_MASK) && (flags & FLAG_JSON))
+ flags |= FLAG_SHOW_MODERN_INFO;
+
+ if (geo_setup() == 0 && (flags & FLAG_SHOW_ALL_INFO))
+ flags |= FLAG_GET_GEOIP;
+
+ if (bit_count(app) > 1) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: you cannot mix these options\n");
+ return 1;
+ }
+
+ if ((flags & FLAG_IPV6) && (flags & FLAG_IPV4)) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: you cannot specify both IPv4 and IPv6\n");
+ return 1;
+ }
+
+ /* if there is a : in the address, it is an IPv6 address.
+ * Note that we allow -4, and -6 to be given explicitly, so
+ * that the tool can be used to check for a valid IPv4 or IPv6
+ * address.
+ */
+ if ((flags & FLAG_IPV4) == 0 && ipStr && strchr(ipStr, ':') != NULL) {
+ flags |= FLAG_IPV6;
+ }
+
+ switch (app) {
+ case APP_VERSION:
+ printf("ipcalc %s\n", VERSION);
+ return 0;
+ case APP_DEAGGREGATE:
+ deaggregate(ipStr, flags);
+ return 0;
+ case APP_SPLIT:
+ case APP_CHECK_ADDRESS:
+ case APP_SHOW_INFO:
+ /* These are handled lower into the info app */
+ break;
+ }
+
+ /* The main application which displays information about an address or
+ * a network. */
+ if (flags & FLAG_RANDOM) {
+ if (ipStr) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: provided superfluous parameter '%s'\n", ipStr);
+ return 1;
+ }
+
+ prefix = str_to_prefix(&flags, randomStr, 1);
+ if (prefix < 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: bad %s prefix: %s\n", (flags&FLAG_IPV6)?"IPv6":"IPv4", randomStr);
+ return 1;
+ }
+
+ ipStr = generate_ip_network(prefix, flags);
+ if (ipStr == NULL) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: cannot generate network with prefix: %u\n",
+ prefix);
+ return 1;
+ }
+ }
+
+ if (hostname == NULL && ipStr == NULL) {
+ if (!beSilent) {
+ fprintf(stderr,
+ "ipcalc: ip address expected\n");
+ usage(1);
+ }
+ return 1;
+ }
+
+ /* resolve IP address if a hostname was given */
+ if (hostname) {
+ int family = AF_UNSPEC;
+ if (flags & FLAG_IPV6)
+ family = AF_INET6;
+ else if (flags & FLAG_IPV4)
+ family = AF_INET;
+
+ ipStr = get_ip_address(family, hostname);
+ if (ipStr == NULL) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: could not resolve %s\n", hostname);
+ return 1;
+ }
+
+ if ((flags & FLAG_IPV4) == 0 && strchr(ipStr, ':') != NULL) {
+ flags |= FLAG_IPV6;
+ }
+ }
+
+ if (chptr) {
+ if ((flags & FLAG_IPV6) == 0) {
+ prefixStr = chptr;
+ } else {
+ if (!beSilent) {
+ fprintf(stderr, "ipcalc: unexpected argument: %s\n",
+ chptr);
+ usage(1);
+ }
+ return 1;
+ }
+ }
+
+ if (prefixStr == NULL && strchr(ipStr, '/') != NULL) {
+ prefixStr = strchr(ipStr, '/');
+ *prefixStr = '\0'; /* fix up ipStr */
+ prefixStr++;
+ }
+
+ if (prefixStr != NULL) {
+ prefix = str_to_prefix(&flags, prefixStr, 0);
+ if (prefix < 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: bad %s prefix: %s\n", (flags & FLAG_IPV6)?"IPv6":"IPv4", prefixStr);
+ return 1;
+ }
+ }
+
+ if (flags & FLAG_IPV6) {
+ r = get_ipv6_info(ipStr, prefix, &info, flags);
+ } else {
+ if ((flags & FLAG_SHOW_BROADCAST) || (flags & FLAG_SHOW_NETWORK) || (flags & FLAG_SHOW_PREFIX)) {
+ if (netmaskStr && prefix >= 0) {
+ if (!beSilent) {
+ fprintf(stderr,
+ "ipcalc: both netmask and prefix specified\n");
+ usage(1);
+ }
+ return 1;
+ }
+ }
+
+ if (prefix == -1 && netmaskStr) {
+ prefix = ipv4_mask_to_int(netmaskStr);
+ if (prefix < 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: bad IPv4 prefix: %s\n", prefixStr);
+ return 1;
+ }
+ }
+ r = get_ipv4_info(ipStr, prefix, &info, flags);
+ }
+
+ if (r < 0) {
+ return 1;
+ }
+
+ switch (app) {
+ case APP_SPLIT:
+ splitPrefix = str_to_prefix(&flags, splitStr, 1);
+ if (splitPrefix < 0) {
+ if (!beSilent)
+ fprintf(stderr,
+ "ipcalc: bad %s prefix: %s\n", (flags & FLAG_IPV6)?"IPv6":"IPv4", splitStr);
+ return 1;
+ }
+
+ if (flags & FLAG_IPV6) {
+ show_split_networks_v6(splitPrefix, &info, flags);
+ } else {
+ show_split_networks_v4(splitPrefix, &info, flags);
+ }
+ return 0;
+ case APP_CHECK_ADDRESS:
+ return 0;
+ default:
+ break;
+ }
+
+ if (isatty(STDOUT_FILENO) != 0)
+ colors = 1;
+
+ /* we know what we want to display now, so display it. */
+ if (flags & FLAG_SHOW_MODERN_INFO) {
+ unsigned single_host = 0;
+
+ if (((flags & FLAG_IPV6) && info.prefix == 128) ||
+ (!(flags & FLAG_IPV6) && info.prefix == 32)) {
+ single_host = 1;
+ }
+
+ output_start(&jsonchain);
+
+ if ((!randomStr || single_host) &&
+ (single_host || strcmp(info.network, info.ip) != 0)) {
+ if (info.expanded_ip) {
+ pretty_printf(&jsonchain,"Full Address:\t", FULL_ADDRESS_NAME, "%s", info.expanded_ip);
+ }
+ pretty_printf(&jsonchain, "Address:\t", ADDRESS_NAME, "%s", info.ip);
+ }
+
+ if (single_host && info.hostname)
+ pretty_printf(&jsonchain, "Hostname:\t", HOSTNAME_NAME, "%s", info.hostname);
+
+ if (!single_host || (flags & FLAG_JSON)) {
+ if (! (flags & FLAG_JSON)) {
+ if (info.expanded_network) {
+ pretty_printf(&jsonchain, "Full Network:\t", FULL_NETWORK_NAME, "%s/%u", info.expanded_network, info.prefix);
+ }
+ pretty_printf(&jsonchain, "Network:\t", NETWORK_NAME, "%s/%u", info.network, info.prefix);
+ pretty_printf(&jsonchain, "Netmask:\t", NETMASK_NAME, "%s = %u", info.netmask, info.prefix);
+ }
+ else {
+ if (info.expanded_network) {
+ pretty_printf(&jsonchain, "Full Network:\t", FULL_NETWORK_NAME, "%s", info.expanded_network);
+ }
+ pretty_printf(&jsonchain, "Network:\t", NETWORK_NAME, "%s", info.network);
+ pretty_printf(&jsonchain, "Netmask:\t", NETMASK_NAME, "%s", info.netmask);
+ pretty_printf(&jsonchain, "Prefix:\t", PREFIX_NAME, "%u", info.prefix);
+ }
+
+
+ if (info.broadcast)
+ pretty_printf(&jsonchain, "Broadcast:\t", BROADCAST_NAME, "%s", info.broadcast);
+ }
+
+ if ((flags & FLAG_SHOW_ALL_INFO) && info.reverse_dns)
+ pretty_printf(&jsonchain, "Reverse DNS:\t", REVERSEDNS_NAME, "%s", info.reverse_dns);
+
+ if (!single_host || (flags & FLAG_JSON)) {
+ output_separate(&jsonchain);
+
+ if (info.type)
+ pretty_dist_printf(&jsonchain, "Address space:\t", ADDRSPACE_NAME, "%s", info.type);
+
+ if ((flags & FLAG_SHOW_ALL_INFO) && info.class)
+ pretty_dist_printf(&jsonchain, "Address class:\t", ADDRCLASS_NAME, "%s", info.class);
+
+ if (info.hostmin)
+ pretty_printf(&jsonchain, "HostMin:\t", MINADDR_NAME, "%s", info.hostmin);
+
+ if (info.hostmax)
+ pretty_printf(&jsonchain, "HostMax:\t", MAXADDR_NAME, "%s", info.hostmax);
+
+ if ((flags & FLAG_IPV6) && info.prefix < 112 && !(flags & FLAG_JSON))
+ pretty_printf(&jsonchain, "Hosts/Net:\t", ADDRESSES_NAME, "2^(%u) = %s", 128-info.prefix, info.hosts);
+ else
+ pretty_printf(&jsonchain, "Hosts/Net:\t", ADDRESSES_NAME, "%s", info.hosts);
+
+ } else {
+
+ if (info.type)
+ pretty_dist_printf(&jsonchain, "Address space:\t", ADDRSPACE_NAME, "%s", info.type);
+
+ if ((flags & FLAG_SHOW_ALL_INFO) && info.class)
+ pretty_dist_printf(&jsonchain, "Address class:\t", ADDRCLASS_NAME, "%s", info.class);
+ }
+
+ if (info.geoip_country || info.geoip_city || info.geoip_coord) {
+ output_separate(&jsonchain);
+
+ if (info.geoip_ccode)
+ pretty_dist_printf(&jsonchain, "Country code:\t", COUNTRYCODE_NAME, "%s", info.geoip_ccode);
+ if (info.geoip_country)
+ pretty_dist_printf(&jsonchain, "Country:\t", COUNTRY_NAME, "%s", info.geoip_country);
+ if (info.geoip_city)
+ pretty_dist_printf(&jsonchain, "City:\t\t", CITY_NAME, "%s", info.geoip_city);
+ if (info.geoip_coord)
+ pretty_dist_printf(&jsonchain, "Coordinates:\t", COORDINATES_NAME, "%s", info.geoip_coord);
+ }
+
+ output_stop(&jsonchain);
+
+ } else if (!(flags & FLAG_SHOW_MODERN_INFO)) {
+
+ if (flags & FLAG_SHOW_ADDRESS) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(ADDRESS_NAME"=");
+ }
+ printf("%s\n", info.ip);
+ }
+
+ if (flags & FLAG_SHOW_NETMASK) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(NETMASK_NAME"=");
+ }
+ printf("%s\n", info.netmask);
+ }
+
+ if (flags & FLAG_SHOW_PREFIX) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(PREFIX_NAME"=");
+ }
+ printf("%u\n", info.prefix);
+ }
+
+ if ((flags & FLAG_SHOW_BROADCAST) && !(flags & FLAG_IPV6)) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(BROADCAST_NAME"=");
+ }
+ printf("%s\n", info.broadcast);
+ }
+
+ if (flags & FLAG_SHOW_NETWORK) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(NETWORK_NAME"=");
+ }
+ printf("%s\n", info.network);
+ }
+
+ if (flags & FLAG_SHOW_REVERSE) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(REVERSEDNS_NAME"=");
+ }
+ printf("%s\n", info.reverse_dns);
+ }
+
+ if ((flags & FLAG_SHOW_MINADDR) && info.hostmin) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(MINADDR_NAME"=");
+ }
+ printf("%s\n", info.hostmin);
+ }
+
+ if ((flags & FLAG_SHOW_MAXADDR) && info.hostmax) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(MAXADDR_NAME"=");
+ }
+ printf("%s\n", info.hostmax);
+ }
+
+ if ((flags & FLAG_SHOW_ADDRSPACE) && info.type) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(ADDRSPACE_NAME"=");
+ }
+ if (strchr(info.type, ' ') != NULL)
+ printf("\"%s\"\n", info.type);
+ else
+ printf("%s\n", info.type);
+ }
+
+ if ((flags & FLAG_SHOW_ADDRESSES) && info.hosts[0]) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(ADDRESSES_NAME"=");
+ }
+ if (strchr(info.hosts, ' ') != NULL)
+ printf("\"%s\"\n", info.hosts);
+ else
+ printf("%s\n", info.hosts);
+ }
+
+ if ((flags & FLAG_RESOLVE_HOST) && info.hostname) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(HOSTNAME_NAME"=");
+ }
+ printf("%s\n", info.hostname);
+ }
+
+ if (flags & FLAG_RESOLVE_IP) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(ADDRESS_NAME"=");
+ }
+ printf("%s\n", ipStr);
+ }
+
+ if ((flags & FLAG_SHOW_GEOIP) == FLAG_SHOW_GEOIP) {
+ if (info.geoip_ccode) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(COUNTRYCODE_NAME"=");
+ }
+ printf("%s\n", info.geoip_ccode);
+ }
+ if (info.geoip_country) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(COUNTRY_NAME"=");
+ }
+ if (strchr(info.geoip_country, ' ') != NULL)
+ printf("\"%s\"\n", info.geoip_country);
+ else
+ printf("%s\n", info.geoip_country);
+ }
+ if (info.geoip_city) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(CITY_NAME"=");
+ }
+ if (strchr(info.geoip_city, ' ') != NULL) {
+ printf("\"%s\"\n", info.geoip_city);
+ } else {
+ printf("%s\n", info.geoip_city);
+ }
+ }
+ if (info.geoip_coord) {
+ if (! (flags & FLAG_NO_DECORATE)) {
+ printf(COORDINATES_NAME"=");
+ }
+ printf("\"%s\"\n", info.geoip_coord);
+ }
+ }
+ }
+
+ return 0;
+}
+