2020#include "ip6string.h"
2121
2222static uint16_t hex (const char * p );
23+ static bool is_hex (char c );
2324
2425/**
2526 * Convert numeric IPv6 address string to a binary.
2627 * IPv4 tunnelling addresses are not covered.
2728 * \param ip6addr IPv6 address in string format.
2829 * \param len Length of ipv6 string.
2930 * \param dest buffer for address. MUST be 16 bytes.
31+ * \return boolean set to true if conversion succeed, false if it didn't
3032 */
31- void stoip6 (const char * ip6addr , size_t len , void * dest )
33+ bool stoip6 (const char * ip6addr , size_t len , void * dest )
3234{
3335 uint8_t * addr ;
3436 const char * p , * q ;
@@ -37,23 +39,45 @@ void stoip6(const char *ip6addr, size_t len, void *dest)
3739 addr = dest ;
3840
3941 if (len > 39 ) { // Too long, not possible. We do not support IPv4-mapped IPv6 addresses
40- return ;
42+ goto error ;
4143 }
4244
4345 // First go forward the string, until end, noting :: position if any
44- for (field_no = 0 , p = ip6addr ; (len > (size_t )(p - ip6addr )) && * p && field_no < 8 ; p = q + 1 ) {
45- q = p ;
46- // Seek for ':' or end
47- while (* q && (* q != ':' )) {
48- q ++ ;
46+ // We're decrementing `len` as we go forward, and stop when it reaches 0
47+ for (field_no = 0 , p = ip6addr ; len && * p ; p = q + 1 ) {
48+
49+ for (q = p ; len && * q && (* q != ':' ); len -= 1 ) { // Seek for ':' or end
50+ if (!is_hex (* q ++ )) { // There must only be hex characters besides ':'
51+ goto error ;
52+ }
53+ }
54+
55+ if ((q - p ) > 4 ) { // We can't have more than 4 hex digits per segment
56+ goto error ;
57+ }
58+
59+ if (field_no == 8 ) { // If the address goes farther than 8 segments
60+ goto error ;
4961 }
50- //Convert and write this part, (high-endian AKA network byte order)
62+
63+ // Convert and write this part, (high-endian AKA network byte order)
5164 addr = common_write_16_bit (hex (p ), addr );
5265 field_no ++ ;
53- //Check if we reached "::"
54- if ((len > (size_t )(q - ip6addr )) && * q && (q [0 ] == ':' ) && (q [1 ] == ':' )) {
55- coloncolon = field_no ;
56- q ++ ;
66+
67+ // We handle the colons
68+ if (len ) {
69+ // Check if we reached "::"
70+ if (q [0 ] == ':' && q [1 ] == ':' ) {
71+ if (coloncolon != -1 ) { // We are not supposed to see "::" more than once per address
72+ goto error ;
73+ }
74+ coloncolon = field_no ;
75+ q ++ ;
76+ len -= 2 ;
77+ }
78+ else {
79+ len -= 1 ;
80+ }
5781 }
5882 }
5983
@@ -65,19 +89,76 @@ void stoip6(const char *ip6addr, size_t len, void *dest)
6589 addr = dest ;
6690 memmove (addr + head_size + inserted_size , addr + head_size , tail_size );
6791 memset (addr + head_size , 0 , inserted_size );
68- } else if (field_no != 8 ) {
69- /* Should really report an error if we didn't get 8 fields */
70- memset (addr , 0 , 16 - field_no * 2 );
92+ } else if (field_no != 8 ) { // Report an error if we didn't get 8 fields
93+ goto error ;
7194 }
95+ return true;
96+
97+ error :
98+ // Fill the output buffer with 0 so we stick to the old failure behavior.
99+ // We are however more agressive and wipe the entire address, and do so more often.
100+ memset (dest , 0 , 16 );
101+ return false;
72102}
73- unsigned char sipv6_prefixlength (const char * ip6addr )
103+
104+ unsigned char sipv6_prefixlength (const char * ip6addr )
74105{
75106 char * ptr = strchr (ip6addr , '/' );
76107 if (ptr ) {
77108 return (unsigned char )strtoul (ptr + 1 , 0 , 10 );
78109 }
79110 return 0 ;
80111}
112+
113+ int stoip6_prefix (const char * ip6addr , void * dest , int_fast16_t * prefix_len_out )
114+ {
115+ size_t addr_len , total_len ;
116+ int_fast16_t prefix_length ;
117+
118+ if (prefix_len_out ) {
119+ * prefix_len_out = -1 ;
120+ }
121+
122+ total_len = addr_len = strlen (ip6addr );
123+ const char * ptr = strchr (ip6addr , '/' );
124+ if (ptr ) {
125+ addr_len = ptr - ip6addr ;
126+ if (prefix_len_out ) {
127+ if (total_len - addr_len > 3 ) {
128+ /* too many digits in prefix */
129+ return -1 ;
130+ }
131+
132+ prefix_length = strtoul (ptr + 1 , 0 , 10 );
133+ if (prefix_length < 0 || prefix_length > 128 ) {
134+ /* prefix value illegal */
135+ return -1 ;
136+ }
137+
138+ * prefix_len_out = prefix_length ;
139+ }
140+ }
141+
142+ if (!stoip6 (ip6addr , addr_len , dest )) {
143+ /* parser failure */
144+ return -1 ;
145+ }
146+
147+ return 0 ;
148+ }
149+
150+ static bool is_hex (char c )
151+ {
152+ // 'A' (0x41) and 'a' (0x61) are mapped in the ASCII table in such a way that masking the 0x20 bit turn 'a' in 'A'
153+ if ((c & ~0x20 ) >= 'A' && (c & ~0x20 ) <= 'F' )
154+ return true;
155+
156+ if (c >= '0' && c <= '9' )
157+ return true;
158+
159+ return false;
160+ }
161+
81162static uint16_t hex (const char * p )
82163{
83164 uint16_t val = 0 ;
0 commit comments