Line data Source code
1 : #include "fd_ip6.h"
2 : #include "fd_ip4.h"
3 : #include "../fd_util.h"
4 :
5 : #include <net/if.h> /* if_nametoindex */
6 :
7 : /* hex_val returns the value of a hex digit, or -1 if c is not a hex
8 : digit. */
9 :
10 : static int
11 2994 : hex_val( int c ) {
12 2994 : if( c>='0' && c<='9' ) return c-'0';
13 1134 : if( c>='a' && c<='f' ) return c-'a'+10;
14 582 : if( c>='A' && c<='F' ) return c-'A'+10;
15 558 : return -1;
16 582 : }
17 :
18 : /* ip4_parse parses a dotted quad in [s,end). Returns 1 on success and
19 : stores the address in network byte order to out, otherwise 0. */
20 :
21 : static int
22 : ip4_parse( char const * s,
23 : char const * end,
24 66 : uint * out ) {
25 66 : uchar octet[ 4 ];
26 306 : for( ulong i=0UL; i<4UL; i++ ) {
27 261 : if( i ) {
28 195 : if( s>=end || *s!='.' ) return 0;
29 186 : s++;
30 186 : }
31 252 : uint val = 0U;
32 252 : int dig = 0;
33 252 : int lead_zero = ( s<end && *s=='0' );
34 579 : while( s<end && *s>='0' && *s<='9' ) {
35 327 : val = val*10U + (uint)( *s-'0' );
36 327 : s++;
37 327 : if( ++dig>3 ) return 0;
38 327 : }
39 : /* Reject leading zeroes, which are ambiguous (octal or decimal?) */
40 252 : if( !dig || val>255U || ( lead_zero && dig>1 ) ) return 0;
41 240 : octet[ i ] = (uchar)val;
42 240 : }
43 45 : if( s!=end ) return 0;
44 39 : memcpy( out, octet, 4UL );
45 39 : return 1;
46 45 : }
47 :
48 : /* ip6_parse parses an IPv6 address literal (RFC 4291 section 2.2) in
49 : [s,end), including the zero compression ("::") and embedded IPv4
50 : ("::ffff:1.2.3.4") forms. Returns 1 on success and stores the
51 : address in network byte order to out, otherwise 0. */
52 :
53 : static int
54 : ip6_parse( char const * s,
55 : char const * end,
56 261 : uchar out[ 16 ] ) {
57 261 : uchar addr[ 16 ] = {0};
58 261 : int cnt = 0; /* number of 16-bit groups parsed */
59 261 : int gap = -1; /* group index at which "::" appeared, or -1 */
60 :
61 261 : if( s>=end ) return 0;
62 :
63 249 : if( *s==':' ) { /* leading "::" (the only way an address may start with ':') */
64 90 : s++;
65 90 : if( s>=end || *s!=':' ) return 0;
66 84 : s++;
67 84 : gap = 0;
68 84 : }
69 :
70 243 : int expect_sep = 0;
71 957 : while( s<end ) {
72 :
73 837 : if( expect_sep ) {
74 534 : if( *s!=':' ) return 0;
75 531 : s++;
76 531 : if( s<end && *s==':' ) { /* "::" */
77 84 : if( gap>=0 ) return 0; /* at most one gap */
78 78 : s++;
79 78 : gap = cnt;
80 78 : expect_sep = 0;
81 78 : continue;
82 84 : }
83 447 : if( s>=end ) return 0; /* trailing ':' */
84 447 : }
85 :
86 : /* Find the end of this group and check whether it is an embedded
87 : IPv4 address */
88 :
89 744 : char const * tok_end = s;
90 744 : int is_ip4 = 0;
91 2619 : while( tok_end<end && *tok_end!=':' ) {
92 1875 : is_ip4 |= ( *tok_end=='.' );
93 1875 : tok_end++;
94 1875 : }
95 :
96 744 : if( is_ip4 ) {
97 75 : uint ip4;
98 75 : if( cnt>6 || tok_end!=end ) return 0; /* must be the last two groups */
99 66 : if( !ip4_parse( s, tok_end, &ip4 ) ) return 0;
100 39 : memcpy( addr+2*cnt, &ip4, 4UL );
101 39 : cnt += 2;
102 39 : s = tok_end;
103 39 : break;
104 66 : }
105 :
106 669 : uint val = 0U;
107 669 : int dig = 0;
108 1881 : while( s<end && hex_val( *s )>=0 ) {
109 1218 : val = ( val<<4 ) | (uint)hex_val( *s );
110 1218 : s++;
111 1218 : if( ++dig>4 ) return 0;
112 1218 : }
113 663 : if( !dig || cnt>7 ) return 0;
114 636 : addr[ 2*cnt ] = (uchar)( val>>8 );
115 636 : addr[ 2*cnt+1 ] = (uchar)( val );
116 636 : cnt++;
117 636 : expect_sep = 1;
118 :
119 636 : }
120 :
121 159 : if( gap<0 ) {
122 42 : if( cnt!=8 ) return 0;
123 117 : } else {
124 117 : if( cnt>=8 ) return 0; /* "::" must cover at least one group */
125 : /* Move the groups that follow the gap to the end of the address */
126 111 : int tail = cnt-gap;
127 111 : if( tail ) memmove( addr+16-2*tail, addr+2*gap, (ulong)( 2*tail ) );
128 111 : memset( addr+2*gap, 0, (ulong)( 16-2*gap-2*tail ) );
129 111 : }
130 :
131 138 : memcpy( out, addr, 16UL );
132 138 : return 1;
133 159 : }
134 :
135 : /* zone_parse resolves a zone ID (interface name or numeric interface
136 : index) in [s,end) to an interface index. Returns 0 on failure. */
137 :
138 : static uint
139 : zone_parse( char const * s,
140 30 : char const * end ) {
141 30 : if( s>=end ) return 0U;
142 :
143 27 : int numeric = 1;
144 105 : for( char const * p=s; p<end; p++ ) numeric &= ( *p>='0' && *p<='9' );
145 :
146 27 : if( numeric ) {
147 12 : ulong idx = 0UL;
148 48 : for( char const * p=s; p<end; p++ ) {
149 39 : idx = idx*10UL + (ulong)( *p-'0' );
150 39 : if( idx>UINT_MAX ) return 0U;
151 39 : }
152 9 : return (uint)idx;
153 12 : }
154 :
155 15 : if( (ulong)( end-s )>=IF_NAMESIZE ) return 0U;
156 15 : char name[ IF_NAMESIZE ];
157 15 : memcpy( name, s, (ulong)( end-s ) );
158 15 : name[ end-s ] = '\0';
159 15 : return if_nametoindex( name ); /* 0 if no such interface */
160 15 : }
161 :
162 : int
163 : fd_cstr_to_ip6_addr( char const * s,
164 261 : fd_ip6_addr_t * out ) {
165 :
166 : /* Split off the zone ID, if any */
167 :
168 261 : char const * end = s;
169 261 : char const * zone = NULL;
170 3051 : while( *end ) {
171 2835 : if( *end=='%' ) { zone = end+1; break; }
172 2790 : end++;
173 2790 : }
174 :
175 261 : fd_ip6_addr_t addr = {0};
176 261 : if( FD_UNLIKELY( !ip6_parse( s, end, addr.addr ) ) ) return 0;
177 :
178 138 : if( zone ) {
179 : /* A zone ID only identifies an interface for addresses that are not
180 : globally scoped. Reject it elsewhere, as the kernel would
181 : silently ignore it. */
182 36 : if( FD_UNLIKELY( !fd_ip6_addr_is_scoped( addr.addr ) ) ) return 0;
183 30 : char const * zone_end = zone;
184 108 : while( *zone_end ) zone_end++;
185 30 : addr.scope_id = zone_parse( zone, zone_end );
186 30 : if( FD_UNLIKELY( !addr.scope_id ) ) return 0;
187 30 : }
188 :
189 117 : *out = addr;
190 117 : return 1;
191 138 : }
192 :
193 : int
194 : fd_cstr_to_ip46_addr( char const * s,
195 18 : fd_ip6_addr_t * out ) {
196 18 : uint ip4_addr;
197 18 : if( fd_cstr_to_ip4_addr( s, &ip4_addr ) ) {
198 9 : fd_ip6_addr_t addr = {0};
199 9 : fd_ip6_addr_ip4_mapped( addr.addr, ip4_addr );
200 9 : *out = addr;
201 9 : return 1;
202 9 : }
203 9 : return fd_cstr_to_ip6_addr( s, out );
204 18 : }
205 :
206 : /* ip4_cstr appends a dotted quad to p */
207 :
208 : static char *
209 : ip4_cstr( char * p,
210 12 : uchar const * octet ) {
211 60 : for( ulong i=0UL; i<4UL; i++ ) {
212 48 : if( i ) p = fd_cstr_append_char( p, '.' );
213 48 : p = fd_cstr_append_printf( p, "%u", (uint)octet[ i ] );
214 48 : }
215 12 : return p;
216 12 : }
217 :
218 : char *
219 : fd_ip6_addr_cstr( char buf[ static FD_IP6_ADDR_CSTR_MAX ],
220 51 : fd_ip6_addr_t const * addr ) {
221 51 : uchar const * a = addr->addr;
222 51 : int ip4 = fd_ip6_addr_is_ip4_mapped( a );
223 :
224 : /* An IPv4 address prints as a dotted quad */
225 :
226 51 : if( ip4 && !addr->scope_id ) {
227 9 : char * p = fd_cstr_init( buf );
228 9 : p = ip4_cstr( p, a+12 );
229 9 : fd_cstr_fini( p );
230 9 : return buf;
231 9 : }
232 :
233 42 : ushort group[ 8 ];
234 378 : for( ulong i=0UL; i<8UL; i++ ) group[ i ] = (ushort)( ( (uint)a[ 2*i ]<<8 ) | a[ 2*i+1 ] );
235 :
236 : /* Find the longest run of zero groups to compress, leftmost run
237 : winning ties. Runs of a single group are not compressed
238 : (RFC 5952 section 4.2). */
239 :
240 42 : int gap = -1, gap_len = 1;
241 213 : for( int i=0; i<8; ) {
242 171 : if( group[ i ] ) { i++; continue; }
243 45 : int j = i;
244 255 : while( j<8 && !group[ j ] ) j++;
245 45 : if( j-i>gap_len ) { gap = i; gap_len = j-i; }
246 45 : i = j;
247 45 : }
248 :
249 42 : char * p = fd_cstr_init( buf );
250 42 : int colon = 0;
251 42 : p = fd_cstr_append_char( p, '[' );
252 213 : for( int i=0; i<8; ) {
253 174 : if( i==gap ) {
254 36 : p = fd_cstr_append_text( p, "::", 2UL );
255 36 : i += gap_len;
256 36 : colon = 0;
257 36 : continue;
258 36 : }
259 138 : if( colon ) p = fd_cstr_append_char( p, ':' );
260 138 : if( ip4 && i==6 ) { /* IPv4-mapped addresses use the mixed notation (RFC 5952 section 5) */
261 3 : p = ip4_cstr( p, a+12 );
262 3 : break;
263 3 : }
264 135 : p = fd_cstr_append_printf( p, "%x", (uint)group[ i ] );
265 135 : colon = 1;
266 135 : i++;
267 135 : }
268 :
269 42 : if( addr->scope_id ) p = fd_cstr_append_printf( p, "%%%u", addr->scope_id );
270 42 : p = fd_cstr_append_char( p, ']' );
271 42 : fd_cstr_fini( p );
272 :
273 42 : return buf;
274 51 : }
|