20
20
#include "ip6string.h"
21
21
22
22
static uint16_t hex (const char * p );
23
+ static bool is_hex (char c );
23
24
24
25
/**
25
26
* Convert numeric IPv6 address string to a binary.
26
27
* IPv4 tunnelling addresses are not covered.
27
28
* \param ip6addr IPv6 address in string format.
28
29
* \param len Length of ipv6 string.
29
30
* \param dest buffer for address. MUST be 16 bytes.
31
+ * \return boolean set to true if conversion succeed, false if it didn't
30
32
*/
31
- void stoip6 (const char * ip6addr , size_t len , void * dest )
33
+ bool stoip6 (const char * ip6addr , size_t len , void * dest )
32
34
{
33
35
uint8_t * addr ;
34
36
const char * p , * q ;
@@ -37,23 +39,45 @@ void stoip6(const char *ip6addr, size_t len, void *dest)
37
39
addr = dest ;
38
40
39
41
if (len > 39 ) { // Too long, not possible. We do not support IPv4-mapped IPv6 addresses
40
- return ;
42
+ goto error ;
41
43
}
42
44
43
45
// 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 ;
49
61
}
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)
51
64
addr = common_write_16_bit (hex (p ), addr );
52
65
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
+ }
57
81
}
58
82
}
59
83
@@ -65,19 +89,76 @@ void stoip6(const char *ip6addr, size_t len, void *dest)
65
89
addr = dest ;
66
90
memmove (addr + head_size + inserted_size , addr + head_size , tail_size );
67
91
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 ;
71
94
}
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;
72
102
}
73
- unsigned char sipv6_prefixlength (const char * ip6addr )
103
+
104
+ unsigned char sipv6_prefixlength (const char * ip6addr )
74
105
{
75
106
char * ptr = strchr (ip6addr , '/' );
76
107
if (ptr ) {
77
108
return (unsigned char )strtoul (ptr + 1 , 0 , 10 );
78
109
}
79
110
return 0 ;
80
111
}
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
+
81
162
static uint16_t hex (const char * p )
82
163
{
83
164
uint16_t val = 0 ;
0 commit comments