diff options
Diffstat (limited to 'ipcalc.c')
-rw-r--r-- | ipcalc.c | 1990 |
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; +} + |