cidr2list.c (3579B)
- // Copyright © 2025 Haelwenn (lanodan) Monnier <contact+utils-cidr@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 202405L
- #include <ctype.h>
- #include <endian.h>
- #include <errno.h>
- #include <inttypes.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- struct inet4_cidr
- {
- uint32_t addr;
- uint8_t cidr;
- uint32_t nmask;
- };
- bool
- str_inet4(const char *str, size_t size, struct inet4_cidr *res)
- {
- uint8_t dot[4] = {0, 0, 0, 0};
- uint8_t cidr = 32;
- uint8_t di = 0;
- for(size_t i = 0; i < size; i++)
- {
- if(str[i] == '.')
- {
- di++;
- if(di == 4)
- {
- fprintf(
- stderr, "cird2list: error: Invalid IPv4 address, found more than 4 dots: %s\n", str);
- return false;
- }
- continue;
- }
- if(isdigit(str[i]))
- {
- dot[di] *= 10;
- dot[di] += str[i] - '0';
- continue;
- }
- if(i > 1 && str[i] == '/' && i + 1 < size && isdigit(str[i + 1]))
- {
- i++;
- cidr = str[i] - '0';
- if(i + 1 < size && isdigit(str[i + 1]))
- {
- i++;
- cidr *= 10;
- cidr += str[i] - '0';
- }
- if(cidr > 32)
- {
- fprintf(stderr, "cird2list: error: CIDR suffix (/%d) is higher than /32: %s\n", cidr, str);
- return false;
- }
- break;
- }
- fprintf(stderr, "cird2list: error: Invalid IPv4 address, found '%c' (0x%X)\n", str[i], str[i]);
- return false;
- }
- if(di != 3)
- {
- fprintf(
- stderr, "cird2list: error: Invalid IPv4 address, found %d dots, expected 3: %s\n", di, str);
- return false;
- }
- #if 0
- fprintf(stderr, "cird2list: DEBUG: %s -> %d.%d.%d.%d/%d\n", str, dot[0], dot[1], dot[2], dot[3], cidr);
- #endif
- uint32_t nmask = 0xFFFFFFFFUL;
- switch(cidr)
- {
- // Make sure to avoid overflow
- case 0:
- nmask = 0;
- break;
- // 2^(32-cidr) - 1
- default:
- nmask ^= (1 << (32 - cidr)) - 1;
- }
- #if 0
- fprintf(stderr, "cird2list: DEBUG: nmask (/%d): ", cidr);
- fprintf(stderr, "%d.%d.%d.%d\n", (nmask >> 24) & 0xFF, (nmask >> 16) & 0xFF, (nmask >> 8) & 0xFF, (nmask) & 0xFF);
- #endif
- res->addr = (dot[0] << 24) | (dot[1] << 16) | (dot[2] << 8) | dot[3];
- res->cidr = cidr;
- res->nmask = nmask;
- return true;
- }
- int
- main(void)
- {
- char *buf = NULL;
- size_t buflen = 0;
- while(true)
- {
- errno = 0;
- ssize_t nread = getline(&buf, &buflen, stdin);
- if(nread < 0)
- {
- if(errno == 0) break;
- fprintf(stderr, "cird2list: error: Failed reading line: %s\n", strerror(errno));
- return 1;
- }
- char *line = buf;
- size_t linelen = nread;
- // ltrim
- while(line - buf < nread && isspace(*line))
- line++, linelen--;
- // rtrim
- for(int i = linelen - 1; (i > 0) && isspace(line[i]); i--)
- line[--linelen] = '\0';
- if(linelen <= 0) continue;
- struct inet4_cidr addr;
- if(!str_inet4(line, linelen, &addr)) return 1;
- uint32_t start = addr.addr & addr.nmask;
- #if 0
- fprintf(stderr, "cird2list: DEBUG: start: ");
- fprintf(stderr, "%d.%d.%d.%d\n", (start >> 24) & 0xFF, (start >> 16) & 0xFF, (start >> 8) & 0xFF, start & 0xFF);
- #endif
- uint32_t end = start | (addr.nmask ^ 0xFFFFFFFFUL);
- #if 0
- fprintf(stderr, "cird2list: DEBUG: end: ");
- fprintf(stderr, "%d.%d.%d.%d\n", (end >> 24) & 0xFF, (end >> 16) & 0xFF, (end >> 8) & 0xFF, end & 0xFF);
- #endif
- bool frontier = false;
- if(end == 0xFFFFFFFFUL)
- {
- // Otherwise for-loop overflows and goes loopy due to
- // the `<=` comparison which works for all other cases
- end--;
- frontier = true;
- }
- for(uint32_t ai = start; ai <= end; ai++)
- {
- printf("%d.%d.%d.%d\n", (ai >> 24) & 0xFF, (ai >> 16) & 0xFF, (ai >> 8) & 0xFF, ai & 0xFF);
- }
- if(frontier) puts("255.255.255.255");
- }
- free(buf);
- return 0;
- }