#include "fmt.h" #include "byte.h" #include "scan.h" #include "ip.h" #include "str.h" /** * @file ip6.c * @authors djb, fefe, feh * @ref ucspi-tcp, ucspi-tcp6 * @brief handling of IPv6 addresses */ /** * @brief ip6_fmt * convert IPv6 address to compactified IPv6 address string * @param input: IPv6 char array * output: pointer to IPv6 address string * @return int length of address (ok > 0) */ unsigned int ip6_fmt(char *s,char ip[16]) { unsigned int len; unsigned int i; unsigned int temp, temp0; unsigned int compressing; unsigned int compressed; int j; len = 0; compressing = 0; compressed = 0; for (j = 0; j < 16; j += 2) { if (j == 12 && ip6_isv4mapped(ip)) { len += ip4_fmt(s,ip+12); break; } temp = ((unsigned long) (unsigned char) ip[j] << 8) + (unsigned long) (unsigned char) ip[j+1]; temp0 = 0; if (!compressing && j < 16) temp0 = ((unsigned long) (unsigned char) ip[j+2] << 8) + (unsigned long) (unsigned char) ip[j+3]; if (temp == 0 && temp0 == 0 && !compressed) { if (!compressing) { compressing = 1; if (j == 0) { if (s) *s++ = ':'; ++len; } } } else { if (compressing) { compressing = 0; ++compressed; if (s) *s++ = ':'; ++len; } i = fmt_xlong(s,temp); len += i; if (s) s += i; if (j < 14) { if (s) *s++ = ':'; ++len; } } } if (compressing) { *s++ = ':'; ++len; } return len; } /** * @brief ip6_fmt_flat * convert IPv6 address to IPv6 address string * @param input: IPv6 char array * output: pointer to IPv6 address string * @return int length of address (ok > 0) */ unsigned int ip6_fmt_flat(char *s,char ip[16]) { int i; for (i = 0; i < 16; i++) { *s++ = tohex((unsigned char)ip[i] >> 4); *s++ = tohex((unsigned char)ip[i] & 15); } return 32; } /** * @brief ia6_fmt * convert IPv6 address to inverse DNS nibble format * 1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.arpa * @param input: IPv6 char array * output: pointer to IPv6 address string * @return int length of address */ unsigned int ia6_fmt(char *s,char ip[16]) { unsigned int i; unsigned int len; int j; static char data[] = "0123456789abcdef"; len = 0; for (j = 15; j >= 0; j--) { i = fmt_str(s,&data[ip[j] & 0x0f]); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; i = fmt_str(s,&data[ip[j] >> 4 & 0x0f]); len += i; if (s) s += i; i = fmt_str(s,"."); len += i; if (s) s += i; } i = fmt_str(s,"ip6.arpa."); len += i; if (s) s += i; return len; } /** * @brief ip6_scan_flat * convert IPv6 address string to IPv6 address array * @param input: pointer to IPv6 address string * output: IPv6 char array * @return int length of address (ok > 0) */ unsigned int ip6_scan_flat(const char *s,char ip[16]) { int i, tmp; for (i = 0; i < 16; i++) { tmp = fromhex(*s++); if (tmp < 0) return 0; ip[i] = tmp << 4; tmp = fromhex(*s++); if (tmp < 0) return 0; ip[i] += tmp; } return 32; } /** * @brief ip6_scan * parse compactified IPv6 address string and convert to IPv6 address array * @param input: pointer to IPv6 address string * output: IPv6 char array * @return int length of ip6_address/ip */ unsigned int ip6_scan(const char *s,char ip[16]) { unsigned int i, j; unsigned int len = 0; unsigned long u; char suffix[16]; int prefixlen = 0; int suffixlen = 0; /* Always return IPv4 as IPv4-mapped IPv6 address */ if ((i = ip4_scan(s,ip+12))) { for (len = 0; len < 12; ++len) ip[len] = V4mappedprefix[len]; return i; if (byte_equal(ip+12,4,V4localnet)) { byte_copy(ip,16,V6localnet); return 16; } } byte_zero(ip,16); for (;;) { if (*s == ':') { len++; if (s[1] == ':') { /* Found "::", skip to part 2 */ s += 2; len++; break; } s++; } i = scan_xlong((char *)s,&u); if (!i) return 0; if (prefixlen == 12 && s[i] == '.') { /* the last 4 bytes may be written as IPv4 address */ i = ip4_scan(s,ip+12); if (i) return i+len; else return 0; } ip[prefixlen++] = (u >> 8); ip[prefixlen++] = (u & 255); s += i; len += i; if (prefixlen == 16) return len; } /* part 2, after "::" */ for (;;) { if (*s == ':') { if (suffixlen == 0) break; s++; len++; } else if (suffixlen != 0) break; i = scan_xlong((char *)s,&u); if (!i) { len--; break; } if (suffixlen + prefixlen <= 12 && s[i] == '.') { j = ip4_scan(s,suffix+suffixlen); if (j) { suffixlen += 4; len += j; break; } else prefixlen = 12 - suffixlen; /* make end-of-loop test true */ } suffix[suffixlen++] = (u >> 8); suffix[suffixlen++] = (u & 255); s += i; len += i; if (prefixlen + suffixlen == 16) break; } for (i = 0; i < suffixlen; i++) ip[16 - suffixlen + i] = suffix[i]; return len; } /** * @brief ip6_scanbracket * parse IPv6 string address enclosed in brackets * @param input: pointer to IPv6 address string * output: IPv6 char array * @return int length of ip_address (ok > 0) */ unsigned int ip6_scanbracket(const char *s,char ip[16]) { unsigned int len; if (*s != '[') return 0; len = ip6_scan(s + 1,ip); if (!len) return 0; if (s[len + 1] != ']') return 0; return len + 2; } /** * @brief ip6_ifscan * parse compactified IPv6 address string * concatinated with the interface name: fe80::1%eth0 * @param input: pointer to IPv6 address string * output: IPv6 char array, stralloc interface_name * @return int length of ip6_address/ip */ unsigned int ip6_ifscan(char *s,char ip[16],stralloc *ifname) { int i; int j = 0; int k = 0; if (!stralloc_copys(ifname,"0")) return 0; if ((j = str_chr(s,'%'))) { if ((i = str_chr(s+j+1,' '))) k = i; else if ((i = str_chr(s+j+1,'\n'))) k = i; else if ((i = str_chr(s+j+1,'\t'))) k = i; if (k) s[j+k+1] = '\0'; /* input might contain trailing chars */ if (!stralloc_copys(ifname,s+j+1)) return 0; s[j] = 0; } if (!stralloc_0(ifname)) return 0; return ip6_scan(s,ip); } /** * @brief ip6_cidr * parse compactified IPv6 address string * concatinated with the prefix length: fe80::1/64 * @param input: pointer to IPv6 address string * output: IPv6 char array, long plen * @return int length of ip6_address/ip */ unsigned int ip6_cidr(char *s,char ip[16],unsigned long *plen) { unsigned int j = 0; *plen = 128UL; j = str_chr(s,'/'); if (s[j] == '/') { s[j] = 0; j = scan_ulong(s+j+1,plen); } return ip6_scan((const char *)s,ip); } /** * @brief ip6_bytestring * parse IPv6 address and represent as char string with length prefix * @param input: IPv6 char array, prefix length * output: pointer to stralloc bit string; * @return n: number of bytes, if ok; -1: memory shortage */ unsigned int ip6_bytestring(stralloc *ipstring,char ip[16],int prefix) { int i, j, n = 0; unsigned char lowbyte, highbyte; if (!stralloc_readyplus(ipstring,128)) return -1; if (!stralloc_copys(ipstring,"")) return -1; for (i = 0; i < 16; i++) { lowbyte = (unsigned char) (ip[i]) & 0x0f; highbyte = (unsigned char) (ip[i] >> 4) & 0x0f; for (j = 3; j >= 0; j--) { if (highbyte & (1 << j)) { n++; if (!stralloc_cats(ipstring,"1")) return -1; } else { n++; if (!stralloc_cats(ipstring,"0")) return -1; } prefix--; if (!prefix) goto DONE; } for (j = 3; j >= 0; j--) { if (lowbyte & (1 << j)) { n++; if (!stralloc_cats(ipstring,"1")) return -1; } else { n++; if (!stralloc_cats(ipstring,"0")) return -1; } prefix--; if (!prefix) goto DONE; } } DONE: if (!stralloc_0(ipstring)) return -1; return n; }