/* * 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 . * * Authors: * Nikos Mavrogiannopoulos * Erik Troan * Preston Brown * David Cantrell */ #define _GNU_SOURCE /* asprintf */ #include #include #include #include #include #include #include #include #include /* open */ #include /* open */ #include /* read */ #include #include #include #include /* 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; }