/* * Copyright (c) 1996 by Internet Software Consortium. * Copyright (c) 2015 by Martine Lenders * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /** * @ingroup net_ipv6_addr * @{ * * @file * * @author Paul Vixie * @author Martine Lenders */ #include #include #include "byteorder.h" #include "net/ipv4/addr.h" #include "net/ipv6/addr.h" /* Length of an IPv6 address in 16-bit words */ #define IPV6_ADDR_WORD_LEN (sizeof(ipv6_addr_t) / sizeof(uint16_t)) #define HEX_L "0123456789abcdef" /* based on inet_ntop6() by Paul Vixie */ char *ipv6_addr_to_str(char *result, const ipv6_addr_t *addr, uint8_t result_len) { char tmp[IPV6_ADDR_MAX_STR_LEN], *tp; struct { int16_t base, len; } best = { -1, 0}, cur = { -1, 0}; if ((result == NULL) || (addr == NULL)) { return NULL; } /* * Preprocess: * Find the longest run of 0x0000's in address for :: shorthanding. */ for (uint8_t i = 0; i < IPV6_ADDR_WORD_LEN; i++) { if (addr->u16[i].u16 == 0) { if (cur.base == -1) { cur.base = i; cur.len = 1; } else { cur.len++; } } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) { best = cur; } cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) { best = cur; } } if (best.base != -1 && best.len < 2) { best.base = -1; } /* * Format the result. */ tp = tmp; for (int i = 0; i < ((int)IPV6_ADDR_WORD_LEN);) { /* Are we inside the best run of 0x00's? */ if (i == best.base) { *tp++ = ':'; i += best.len; continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) { *tp++ = ':'; } #ifdef MODULE_IPV4_ADDR /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && addr->u16[5].u16 == 0xffff))) { if (!ipv4_addr_to_str(tp, (const ipv4_addr_t *)&addr->u32[3], sizeof(tmp) - (tp - tmp))) { return (NULL); } tp += strlen(tp); break; } #endif if ((addr->u16[i].u8[0] & 0xf0) != 0x00) { *tp++ = HEX_L[addr->u16[i].u8[0] >> 4]; *tp++ = HEX_L[addr->u16[i].u8[0] & 0x0f]; *tp++ = HEX_L[addr->u16[i].u8[1] >> 4]; } else if ((addr->u16[i].u8[0] & 0x0f) != 0x00) { *tp++ = HEX_L[addr->u16[i].u8[0] & 0x0f]; *tp++ = HEX_L[addr->u16[i].u8[1] >> 4]; } else if ((addr->u16[i].u8[1] & 0xf0) != 0x00) { *tp++ = HEX_L[addr->u16[i].u8[1] >> 4]; } *tp++ = HEX_L[addr->u16[i].u8[1] & 0xf]; i++; } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == IPV6_ADDR_WORD_LEN) { *tp++ = ':'; } *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((size_t)(tp - tmp) > result_len) { return NULL; } strcpy(result, tmp); return result; } /** * @} */