Skip to main content
added 84 characters in body
Source Link
200_success
  • 145.7k
  • 22
  • 191
  • 481

In particular, using a struct in_addr to represent an IPv4 address lets you actually use the parsed result in code that really makes network connections. (A struct in_addr just contains a 32-bit unsigned integer in big-endian format.)

In particular, using a struct in_addr to represent an IPv4 address lets you actually use the parsed result in code that really makes network connections.

In particular, using a struct in_addr to represent an IPv4 address lets you actually use the parsed result in code that really makes network connections. (A struct in_addr just contains a 32-bit unsigned integer in big-endian format.)

Source Link
200_success
  • 145.7k
  • 22
  • 191
  • 481

Reinventing the wheel

Parsing CIDR notation is a somewhat common task, and you are . In fact, there is an inet_net_pton(3) function that does exactly that, available in BSD (including macOS), Linux, and other Unix flavours.

The inet_net_pton() function converts a presentation format Internet network number (that is, printable form as held in a character string) to network format (usually a struct in_addr or some other internal binary representation, in network byte order). It returns the number of bits, either computed based on the class, or specified with /CIDR), or -1 if a failure occurred (in which case errno will have been set. It will be set to ENOENT if the Internet network number was not valid).

Windows does not have an inet_net_pton() function, but it does support the inet_pton() POSIX function, which parses the address but not the netmask.

Even if you want to reinvent the wheel for fun or out of necessity, it pays to see how it is conventionally done, because:

  • You may learn lessons from other programmers' design decisions.
  • Other programmers might have an easier time figuring out your code if it follows the conventional approach.
  • Following the standard interface lets you create interoperable code.

In particular, using a struct in_addr to represent an IPv4 address lets you actually use the parsed result in code that really makes network connections.

Here is a solution based on inet_net_pton(). I think you would agree that it's simpler. Notice that each calculation works on the entire address at once, and each result is easily formatted as a dotted quad using inet_ntop().

#include <inttypes.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>

/**
 * Parses a string in CIDR notation as an IPv4 address and netmask.
 * Returns the number of bits in the netmask if the string is valid.
 * Returns -1 if the string is invalid.
 */
int parse_cidr(const char *cidr, struct in_addr *addr, struct in_addr *mask) {
    int bits = inet_net_pton(AF_INET, cidr, addr, sizeof addr);
    /* Shifting by 32 bits is undefined (http://stackoverflow.com/q/2648764) */
    mask->s_addr = htonl(~(bits == 32 ? 0 : ~0U >> bits));
    return bits; 
}   

/**
 * Formats the IPv4 address in dotted quad notation, using a static buffer.
 */
const char *dotted_quad(const struct in_addr *addr) {
    static char buf[INET_ADDRSTRLEN];
    return inet_ntop(AF_INET, addr, buf, sizeof buf);
}   

int main(int argc, char *argv[]) {
    struct in_addr addr, mask, wildcard, network, broadcast, min, max;
    int64_t num_hosts;
    
    int bits = parse_cidr(argv[1], &addr, &mask);
    if (bits == -1) {
        fprintf(stderr, "Invalid address/netmask: %s\n", argv[1]));
        return 1;
    }

    wildcard = mask;    wildcard.s_addr = ~wildcard.s_addr;
    network = addr;     network.s_addr &= mask.s_addr;
    broadcast = addr;   broadcast.s_addr |= wildcard.s_addr;
    min = network;      min.s_addr = htonl(ntohl(min.s_addr) + 1);
    max = broadcast;    max.s_addr = htonl(ntohl(max.s_addr) - 1);
    num_hosts = (int64_t)ntohl(broadcast.s_addr) - ntohl(network.s_addr) + 1;
    
    printf("> [GENERAL]\n\n");
    printf("Address:---------------------> %s\n", dotted_quad(&addr));
    printf("Network Mask:----------------> %s => %d\n", dotted_quad(&mask), bits);
    printf("Wildcard Mask:---------------> %s\n", dotted_quad(&wildcard));
    printf("Network Address:-------------> %s\n", dotted_quad(&network));
    printf("Broadcast Address:-----------> %s\n", dotted_quad(&broadcast));
    if (num_hosts > 2) {
        printf("Minimum Usable Address:------> %s\n", dotted_quad(&min));
        printf("Maximum Usable Address:------> %s\n", dotted_quad(&max));
        printf("Number of Hosts:-------------> %" PRId64 "\n", num_hosts - 2);
    }   
    printf("Total Hosts: ----------------> %" PRId64 "\n", num_hosts);
    return 0;
}