logo

utils-cidr

utilities to manipulate CIDR ip-ranges git clone https://anongit.hacktivis.me/git/utils-cidr.git

cidr2list.c (3579B)


  1. // Copyright © 2025 Haelwenn (lanodan) Monnier <contact+utils-cidr@hacktivis.me>
  2. // SPDX-License-Identifier: MPL-2.0
  3. #define _POSIX_C_SOURCE 202405L
  4. #include <ctype.h>
  5. #include <endian.h>
  6. #include <errno.h>
  7. #include <inttypes.h>
  8. #include <stdbool.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. struct inet4_cidr
  13. {
  14. uint32_t addr;
  15. uint8_t cidr;
  16. uint32_t nmask;
  17. };
  18. bool
  19. str_inet4(const char *str, size_t size, struct inet4_cidr *res)
  20. {
  21. uint8_t dot[4] = {0, 0, 0, 0};
  22. uint8_t cidr = 32;
  23. uint8_t di = 0;
  24. for(size_t i = 0; i < size; i++)
  25. {
  26. if(str[i] == '.')
  27. {
  28. di++;
  29. if(di == 4)
  30. {
  31. fprintf(
  32. stderr, "cird2list: error: Invalid IPv4 address, found more than 4 dots: %s\n", str);
  33. return false;
  34. }
  35. continue;
  36. }
  37. if(isdigit(str[i]))
  38. {
  39. dot[di] *= 10;
  40. dot[di] += str[i] - '0';
  41. continue;
  42. }
  43. if(i > 1 && str[i] == '/' && i + 1 < size && isdigit(str[i + 1]))
  44. {
  45. i++;
  46. cidr = str[i] - '0';
  47. if(i + 1 < size && isdigit(str[i + 1]))
  48. {
  49. i++;
  50. cidr *= 10;
  51. cidr += str[i] - '0';
  52. }
  53. if(cidr > 32)
  54. {
  55. fprintf(stderr, "cird2list: error: CIDR suffix (/%d) is higher than /32: %s\n", cidr, str);
  56. return false;
  57. }
  58. break;
  59. }
  60. fprintf(stderr, "cird2list: error: Invalid IPv4 address, found '%c' (0x%X)\n", str[i], str[i]);
  61. return false;
  62. }
  63. if(di != 3)
  64. {
  65. fprintf(
  66. stderr, "cird2list: error: Invalid IPv4 address, found %d dots, expected 3: %s\n", di, str);
  67. return false;
  68. }
  69. #if 0
  70. fprintf(stderr, "cird2list: DEBUG: %s -> %d.%d.%d.%d/%d\n", str, dot[0], dot[1], dot[2], dot[3], cidr);
  71. #endif
  72. uint32_t nmask = 0xFFFFFFFFUL;
  73. switch(cidr)
  74. {
  75. // Make sure to avoid overflow
  76. case 0:
  77. nmask = 0;
  78. break;
  79. // 2^(32-cidr) - 1
  80. default:
  81. nmask ^= (1 << (32 - cidr)) - 1;
  82. }
  83. #if 0
  84. fprintf(stderr, "cird2list: DEBUG: nmask (/%d): ", cidr);
  85. fprintf(stderr, "%d.%d.%d.%d\n", (nmask >> 24) & 0xFF, (nmask >> 16) & 0xFF, (nmask >> 8) & 0xFF, (nmask) & 0xFF);
  86. #endif
  87. res->addr = (dot[0] << 24) | (dot[1] << 16) | (dot[2] << 8) | dot[3];
  88. res->cidr = cidr;
  89. res->nmask = nmask;
  90. return true;
  91. }
  92. int
  93. main(void)
  94. {
  95. char *buf = NULL;
  96. size_t buflen = 0;
  97. while(true)
  98. {
  99. errno = 0;
  100. ssize_t nread = getline(&buf, &buflen, stdin);
  101. if(nread < 0)
  102. {
  103. if(errno == 0) break;
  104. fprintf(stderr, "cird2list: error: Failed reading line: %s\n", strerror(errno));
  105. return 1;
  106. }
  107. char *line = buf;
  108. size_t linelen = nread;
  109. // ltrim
  110. while(line - buf < nread && isspace(*line))
  111. line++, linelen--;
  112. // rtrim
  113. for(int i = linelen - 1; (i > 0) && isspace(line[i]); i--)
  114. line[--linelen] = '\0';
  115. if(linelen <= 0) continue;
  116. struct inet4_cidr addr;
  117. if(!str_inet4(line, linelen, &addr)) return 1;
  118. uint32_t start = addr.addr & addr.nmask;
  119. #if 0
  120. fprintf(stderr, "cird2list: DEBUG: start: ");
  121. fprintf(stderr, "%d.%d.%d.%d\n", (start >> 24) & 0xFF, (start >> 16) & 0xFF, (start >> 8) & 0xFF, start & 0xFF);
  122. #endif
  123. uint32_t end = start | (addr.nmask ^ 0xFFFFFFFFUL);
  124. #if 0
  125. fprintf(stderr, "cird2list: DEBUG: end: ");
  126. fprintf(stderr, "%d.%d.%d.%d\n", (end >> 24) & 0xFF, (end >> 16) & 0xFF, (end >> 8) & 0xFF, end & 0xFF);
  127. #endif
  128. bool frontier = false;
  129. if(end == 0xFFFFFFFFUL)
  130. {
  131. // Otherwise for-loop overflows and goes loopy due to
  132. // the `<=` comparison which works for all other cases
  133. end--;
  134. frontier = true;
  135. }
  136. for(uint32_t ai = start; ai <= end; ai++)
  137. {
  138. printf("%d.%d.%d.%d\n", (ai >> 24) & 0xFF, (ai >> 16) & 0xFF, (ai >> 8) & 0xFF, ai & 0xFF);
  139. }
  140. if(frontier) puts("255.255.255.255");
  141. }
  142. free(buf);
  143. return 0;
  144. }