You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
16 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2011-2013, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license details.
  7. * ----------------------------------------------------------------------
  8. */
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include "libcork/core/byte-order.h"
  12. #include "libcork/core/error.h"
  13. #include "libcork/core/net-addresses.h"
  14. #include "libcork/core/types.h"
  15. #ifndef CORK_IP_ADDRESS_DEBUG
  16. #define CORK_IP_ADDRESS_DEBUG 0
  17. #endif
  18. #if CORK_IP_ADDRESS_DEBUG
  19. #include <stdio.h>
  20. #define DEBUG(...) \
  21. do { \
  22. fprintf(stderr, __VA_ARGS__); \
  23. } while (0)
  24. #else
  25. #define DEBUG(...) /* nothing */
  26. #endif
  27. /*-----------------------------------------------------------------------
  28. * IP addresses
  29. */
  30. /*** IPv4 ***/
  31. static inline const char *
  32. cork_ipv4_parse(struct cork_ipv4 *addr, const char *str)
  33. {
  34. const char *ch;
  35. bool seen_digit_in_octet = false;
  36. unsigned int octets = 0;
  37. unsigned int digit = 0;
  38. uint8_t result[4];
  39. for (ch = str; *ch != '\0'; ch++) {
  40. DEBUG("%2u: %c\t", (unsigned int) (ch-str), *ch);
  41. switch (*ch) {
  42. case '0': case '1': case '2': case '3': case '4':
  43. case '5': case '6': case '7': case '8': case '9':
  44. seen_digit_in_octet = true;
  45. digit *= 10;
  46. digit += (*ch - '0');
  47. DEBUG("digit = %u\n", digit);
  48. if (CORK_UNLIKELY(digit > 255)) {
  49. DEBUG("\t");
  50. goto parse_error;
  51. }
  52. break;
  53. case '.':
  54. /* If this would be the fourth octet, it can't have a trailing
  55. * period. */
  56. if (CORK_UNLIKELY(octets == 3)) {
  57. goto parse_error;
  58. }
  59. DEBUG("octet %u = %u\n", octets, digit);
  60. result[octets] = digit;
  61. digit = 0;
  62. octets++;
  63. seen_digit_in_octet = false;
  64. break;
  65. default:
  66. /* Any other character is a parse error. */
  67. goto parse_error;
  68. }
  69. }
  70. /* If we have a valid octet at the end, and that would be the fourth octet,
  71. * then we've got a valid final parse. */
  72. DEBUG("%2u:\t", (unsigned int) (ch-str));
  73. if (CORK_LIKELY(seen_digit_in_octet && octets == 3)) {
  74. #if CORK_IP_ADDRESS_DEBUG
  75. char parsed_ipv4[CORK_IPV4_STRING_LENGTH];
  76. #endif
  77. DEBUG("octet %u = %u\n", octets, digit);
  78. result[octets] = digit;
  79. cork_ipv4_copy(addr, result);
  80. #if CORK_IP_ADDRESS_DEBUG
  81. cork_ipv4_to_raw_string(addr, parsed_ipv4);
  82. DEBUG("\tParsed address: %s\n", parsed_ipv4);
  83. #endif
  84. return ch;
  85. }
  86. parse_error:
  87. DEBUG("parse error\n");
  88. cork_parse_error("Invalid IPv4 address: \"%s\"", str);
  89. return NULL;
  90. }
  91. int
  92. cork_ipv4_init(struct cork_ipv4 *addr, const char *str)
  93. {
  94. return cork_ipv4_parse(addr, str) == NULL? -1: 0;
  95. }
  96. bool
  97. cork_ipv4_equal_(const struct cork_ipv4 *addr1, const struct cork_ipv4 *addr2)
  98. {
  99. return cork_ipv4_equal(addr1, addr2);
  100. }
  101. void
  102. cork_ipv4_to_raw_string(const struct cork_ipv4 *addr, char *dest)
  103. {
  104. snprintf(dest, CORK_IPV4_STRING_LENGTH, "%u.%u.%u.%u",
  105. addr->_.u8[0], addr->_.u8[1], addr->_.u8[2], addr->_.u8[3]);
  106. }
  107. bool
  108. cork_ipv4_is_valid_network(const struct cork_ipv4 *addr,
  109. unsigned int cidr_prefix)
  110. {
  111. uint32_t cidr_mask;
  112. if (cidr_prefix > 32) {
  113. return false;
  114. } else if (cidr_prefix == 32) {
  115. /* This handles undefined behavior for overflow bit shifts. */
  116. cidr_mask = 0;
  117. } else {
  118. cidr_mask = 0xffffffff >> cidr_prefix;
  119. }
  120. return (CORK_UINT32_BIG_TO_HOST(addr->_.u32) & cidr_mask) == 0;
  121. }
  122. /*** IPv6 ***/
  123. int
  124. cork_ipv6_init(struct cork_ipv6 *addr, const char *str)
  125. {
  126. const char *ch;
  127. uint16_t digit = 0;
  128. unsigned int before_count = 0;
  129. uint16_t before_double_colon[8];
  130. uint16_t after_double_colon[8];
  131. uint16_t *dest = before_double_colon;
  132. unsigned int digits_seen = 0;
  133. unsigned int hextets_seen = 0;
  134. bool another_required = true;
  135. bool digit_allowed = true;
  136. bool colon_allowed = true;
  137. bool double_colon_allowed = true;
  138. bool just_saw_colon = false;
  139. for (ch = str; *ch != '\0'; ch++) {
  140. DEBUG("%2u: %c\t", (unsigned int) (ch-str), *ch);
  141. switch (*ch) {
  142. #define process_digit(base) \
  143. /* Make sure a digit is allowed here. */ \
  144. if (CORK_UNLIKELY(!digit_allowed)) { \
  145. goto parse_error; \
  146. } \
  147. /* If we've already seen 4 digits, it's a parse error. */ \
  148. if (CORK_UNLIKELY(digits_seen == 4)) { \
  149. goto parse_error; \
  150. } \
  151. \
  152. digits_seen++; \
  153. colon_allowed = true; \
  154. just_saw_colon = false; \
  155. digit <<= 4; \
  156. digit |= (*ch - (base)); \
  157. DEBUG("digit = %04x\n", digit);
  158. case '0': case '1': case '2': case '3': case '4':
  159. case '5': case '6': case '7': case '8': case '9':
  160. process_digit('0');
  161. break;
  162. case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  163. process_digit('a'-10);
  164. break;
  165. case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  166. process_digit('A'-10);
  167. break;
  168. #undef process_digit
  169. case ':':
  170. /* We can only see a colon immediately after a hextet or as part
  171. * of a double-colon. */
  172. if (CORK_UNLIKELY(!colon_allowed)) {
  173. goto parse_error;
  174. }
  175. /* If this is a double-colon, start parsing hextets into our
  176. * second array. */
  177. if (just_saw_colon) {
  178. DEBUG("double-colon\n");
  179. colon_allowed = false;
  180. digit_allowed = true;
  181. another_required = false;
  182. double_colon_allowed = false;
  183. before_count = hextets_seen;
  184. dest = after_double_colon;
  185. continue;
  186. }
  187. /* If this would end the eighth hextet (regardless of the
  188. * placement of a double-colon), then there can't be a trailing
  189. * colon. */
  190. if (CORK_UNLIKELY(hextets_seen == 8)) {
  191. goto parse_error;
  192. }
  193. /* If this is the very beginning of the string, then we can only
  194. * have a double-colon, not a single colon. */
  195. if (digits_seen == 0 && hextets_seen == 0) {
  196. DEBUG("initial colon\n");
  197. colon_allowed = true;
  198. digit_allowed = false;
  199. just_saw_colon = true;
  200. another_required = true;
  201. continue;
  202. }
  203. /* Otherwise this ends the current hextet. */
  204. DEBUG("hextet %u = %04x\n", hextets_seen, digit);
  205. *(dest++) = CORK_UINT16_HOST_TO_BIG(digit);
  206. digit = 0;
  207. hextets_seen++;
  208. digits_seen = 0;
  209. colon_allowed = double_colon_allowed;
  210. just_saw_colon = true;
  211. another_required = true;
  212. break;
  213. case '.':
  214. {
  215. /* If we see a period, then we must be in the middle of an IPv4
  216. * address at the end of the IPv6 address. */
  217. struct cork_ipv4 *ipv4 = (struct cork_ipv4 *) dest;
  218. DEBUG("Detected IPv4 address %s\n", ch-digits_seen);
  219. /* Ensure that we have space for the two hextets that the IPv4
  220. * address will take up. */
  221. if (CORK_UNLIKELY(hextets_seen >= 7)) {
  222. goto parse_error;
  223. }
  224. /* Parse the IPv4 address directly into our current hextet
  225. * buffer. */
  226. ch = cork_ipv4_parse(ipv4, ch - digits_seen);
  227. if (CORK_LIKELY(ch != NULL)) {
  228. hextets_seen += 2;
  229. digits_seen = 0;
  230. another_required = false;
  231. /* ch now points at the NUL terminator, but we're about to
  232. * increment ch. */
  233. ch--;
  234. break;
  235. }
  236. /* The IPv4 parse failed, so we have an IPv6 parse error. */
  237. goto parse_error;
  238. }
  239. default:
  240. /* Any other character is a parse error. */
  241. goto parse_error;
  242. }
  243. }
  244. /* If we have a valid hextet at the end, and we've either seen a
  245. * double-colon, or we have eight hextets in total, then we've got a valid
  246. * final parse. */
  247. DEBUG("%2u:\t", (unsigned int) (ch-str));
  248. if (CORK_LIKELY(digits_seen > 0)) {
  249. DEBUG("hextet %u = %04x\n\t", hextets_seen, digit);
  250. *(dest++) = CORK_UINT16_HOST_TO_BIG(digit);
  251. hextets_seen++;
  252. } else if (CORK_UNLIKELY(another_required)) {
  253. goto parse_error;
  254. }
  255. if (!double_colon_allowed) {
  256. /* We've seen a double-colon, so use 0000 for any hextets that weren't
  257. * present. */
  258. #if CORK_IP_ADDRESS_DEBUG
  259. char parsed_result[CORK_IPV6_STRING_LENGTH];
  260. #endif
  261. unsigned int after_count = hextets_seen - before_count;
  262. DEBUG("Saw double-colon; %u hextets before, %u after\n",
  263. before_count, after_count);
  264. memset(addr, 0, sizeof(struct cork_ipv6));
  265. memcpy(addr, before_double_colon,
  266. sizeof(uint16_t) * before_count);
  267. memcpy(&addr->_.u16[8-after_count], after_double_colon,
  268. sizeof(uint16_t) * after_count);
  269. #if CORK_IP_ADDRESS_DEBUG
  270. cork_ipv6_to_raw_string(addr, parsed_result);
  271. DEBUG("\tParsed address: %s\n", parsed_result);
  272. #endif
  273. return 0;
  274. } else if (hextets_seen == 8) {
  275. /* No double-colon, so we must have exactly eight hextets. */
  276. #if CORK_IP_ADDRESS_DEBUG
  277. char parsed_result[CORK_IPV6_STRING_LENGTH];
  278. #endif
  279. DEBUG("No double-colon\n");
  280. cork_ipv6_copy(addr, before_double_colon);
  281. #if CORK_IP_ADDRESS_DEBUG
  282. cork_ipv6_to_raw_string(addr, parsed_result);
  283. DEBUG("\tParsed address: %s\n", parsed_result);
  284. #endif
  285. return 0;
  286. }
  287. parse_error:
  288. DEBUG("parse error\n");
  289. cork_parse_error("Invalid IPv6 address: \"%s\"", str);
  290. return -1;
  291. }
  292. bool
  293. cork_ipv6_equal_(const struct cork_ipv6 *addr1, const struct cork_ipv6 *addr2)
  294. {
  295. return cork_ipv6_equal(addr1, addr2);
  296. }
  297. #define NS_IN6ADDRSZ 16
  298. #define NS_INT16SZ 2
  299. void
  300. cork_ipv6_to_raw_string(const struct cork_ipv6 *addr, char *dest)
  301. {
  302. const uint8_t *src = addr->_.u8;
  303. /*
  304. * Note that int32_t and int16_t need only be "at least" large enough
  305. * to contain a value of the specified size. On some systems, like
  306. * Crays, there is no such thing as an integer variable with 16 bits.
  307. * Keep this in mind if you think this function should have been coded
  308. * to use pointer overlays. All the world's not a VAX.
  309. */
  310. char *tp;
  311. struct { int base, len; } best, cur;
  312. unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
  313. int i;
  314. /*
  315. * Preprocess:
  316. * Copy the input (bytewise) array into a wordwise array.
  317. * Find the longest run of 0x00's in src[] for :: shorthanding.
  318. */
  319. memset(words, '\0', sizeof words);
  320. for (i = 0; i < NS_IN6ADDRSZ; i++)
  321. words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
  322. best.base = -1;
  323. best.len = 0;
  324. cur.base = -1;
  325. cur.len = 0;
  326. for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
  327. if (words[i] == 0) {
  328. if (cur.base == -1)
  329. cur.base = i, cur.len = 1;
  330. else
  331. cur.len++;
  332. } else {
  333. if (cur.base != -1) {
  334. if (best.base == -1 || cur.len > best.len)
  335. best = cur;
  336. cur.base = -1;
  337. }
  338. }
  339. }
  340. if (cur.base != -1) {
  341. if (best.base == -1 || cur.len > best.len)
  342. best = cur;
  343. }
  344. if (best.base != -1 && best.len < 2)
  345. best.base = -1;
  346. /*
  347. * Format the result.
  348. */
  349. tp = dest;
  350. for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
  351. /* Are we inside the best run of 0x00's? */
  352. if (best.base != -1 && i >= best.base &&
  353. i < (best.base + best.len)) {
  354. if (i == best.base)
  355. *tp++ = ':';
  356. continue;
  357. }
  358. /* Are we following an initial run of 0x00s or any real hex? */
  359. if (i != 0)
  360. *tp++ = ':';
  361. /* Is this address an encapsulated IPv4? */
  362. if (i == 6 && best.base == 0 &&
  363. (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
  364. tp += sprintf(tp, "%u.%u.%u.%u",
  365. src[12], src[13], src[14], src[15]);
  366. break;
  367. }
  368. tp += sprintf(tp, "%x", words[i]);
  369. }
  370. /* Was it a trailing run of 0x00's? */
  371. if (best.base != -1 && (best.base + best.len) ==
  372. (NS_IN6ADDRSZ / NS_INT16SZ))
  373. *tp++ = ':';
  374. *tp++ = '\0';
  375. }
  376. bool
  377. cork_ipv6_is_valid_network(const struct cork_ipv6 *addr,
  378. unsigned int cidr_prefix)
  379. {
  380. uint64_t cidr_mask[2];
  381. if (cidr_prefix > 128) {
  382. return false;
  383. } else if (cidr_prefix == 128) {
  384. /* This handles undefined behavior for overflow bit shifts. */
  385. cidr_mask[0] = cidr_mask[1] = 0;
  386. } else if (cidr_prefix == 64) {
  387. /* This handles undefined behavior for overflow bit shifts. */
  388. cidr_mask[0] = 0;
  389. cidr_mask[1] = UINT64_C(0xffffffffffffffff);
  390. } else if (cidr_prefix > 64) {
  391. cidr_mask[0] = 0;
  392. cidr_mask[1] = UINT64_C(0xffffffffffffffff) >> (cidr_prefix-64);
  393. } else {
  394. cidr_mask[0] = UINT64_C(0xffffffffffffffff) >> cidr_prefix;
  395. cidr_mask[1] = UINT64_C(0xffffffffffffffff);
  396. }
  397. return (CORK_UINT64_BIG_TO_HOST(addr->_.u64[0] & cidr_mask[0]) == 0) &&
  398. (CORK_UINT64_BIG_TO_HOST(addr->_.u64[1] & cidr_mask[1]) == 0);
  399. }
  400. /*** IP ***/
  401. void
  402. cork_ip_from_ipv4_(struct cork_ip *addr, const void *src)
  403. {
  404. cork_ip_from_ipv4(addr, src);
  405. }
  406. void
  407. cork_ip_from_ipv6_(struct cork_ip *addr, const void *src)
  408. {
  409. cork_ip_from_ipv6(addr, src);
  410. }
  411. int
  412. cork_ip_init(struct cork_ip *addr, const char *str)
  413. {
  414. int rc;
  415. /* Try IPv4 first */
  416. rc = cork_ipv4_init(&addr->ip.v4, str);
  417. if (rc == 0) {
  418. /* successful parse */
  419. addr->version = 4;
  420. return 0;
  421. }
  422. /* Then try IPv6 */
  423. cork_error_clear();
  424. rc = cork_ipv6_init(&addr->ip.v6, str);
  425. if (rc == 0) {
  426. /* successful parse */
  427. addr->version = 6;
  428. return 0;
  429. }
  430. /* Parse error for both address types */
  431. cork_parse_error("Invalid IP address: \"%s\"", str);
  432. return -1;
  433. }
  434. bool
  435. cork_ip_equal_(const struct cork_ip *addr1, const struct cork_ip *addr2)
  436. {
  437. return cork_ip_equal(addr1, addr2);
  438. }
  439. void
  440. cork_ip_to_raw_string(const struct cork_ip *addr, char *dest)
  441. {
  442. switch (addr->version) {
  443. case 4:
  444. cork_ipv4_to_raw_string(&addr->ip.v4, dest);
  445. return;
  446. case 6:
  447. cork_ipv6_to_raw_string(&addr->ip.v6, dest);
  448. return;
  449. default:
  450. strncpy(dest, "<INVALID>", CORK_IP_STRING_LENGTH);
  451. return;
  452. }
  453. }
  454. bool
  455. cork_ip_is_valid_network(const struct cork_ip *addr, unsigned int cidr_prefix)
  456. {
  457. switch (addr->version) {
  458. case 4:
  459. return cork_ipv4_is_valid_network(&addr->ip.v4, cidr_prefix);
  460. case 6:
  461. return cork_ipv6_is_valid_network(&addr->ip.v6, cidr_prefix);
  462. default:
  463. return false;
  464. }
  465. }