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 0 : hex_val( int c ) {
12 0 : if( c>='0' && c<='9' ) return c-'0';
13 0 : if( c>='a' && c<='f' ) return c-'a'+10;
14 0 : if( c>='A' && c<='F' ) return c-'A'+10;
15 0 : return -1;
16 0 : }
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 0 : uint * out ) {
25 0 : uchar octet[ 4 ];
26 0 : for( ulong i=0UL; i<4UL; i++ ) {
27 0 : if( i ) {
28 0 : if( s>=end || *s!='.' ) return 0;
29 0 : s++;
30 0 : }
31 0 : uint val = 0U;
32 0 : int dig = 0;
33 0 : int lead_zero = ( s<end && *s=='0' );
34 0 : while( s<end && *s>='0' && *s<='9' ) {
35 0 : val = val*10U + (uint)( *s-'0' );
36 0 : s++;
37 0 : if( ++dig>3 ) return 0;
38 0 : }
39 : /* Reject leading zeroes, which are ambiguous (octal or decimal?) */
40 0 : if( !dig || val>255U || ( lead_zero && dig>1 ) ) return 0;
41 0 : octet[ i ] = (uchar)val;
42 0 : }
43 0 : if( s!=end ) return 0;
44 0 : memcpy( out, octet, 4UL );
45 0 : return 1;
46 0 : }
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 0 : uchar out[ 16 ] ) {
57 0 : uchar addr[ 16 ] = {0};
58 0 : int cnt = 0; /* number of 16-bit groups parsed */
59 0 : int gap = -1; /* group index at which "::" appeared, or -1 */
60 :
61 0 : if( s>=end ) return 0;
62 :
63 0 : if( *s==':' ) { /* leading "::" (the only way an address may start with ':') */
64 0 : s++;
65 0 : if( s>=end || *s!=':' ) return 0;
66 0 : s++;
67 0 : gap = 0;
68 0 : }
69 :
70 0 : int expect_sep = 0;
71 0 : while( s<end ) {
72 :
73 0 : if( expect_sep ) {
74 0 : if( *s!=':' ) return 0;
75 0 : s++;
76 0 : if( s<end && *s==':' ) { /* "::" */
77 0 : if( gap>=0 ) return 0; /* at most one gap */
78 0 : s++;
79 0 : gap = cnt;
80 0 : expect_sep = 0;
81 0 : continue;
82 0 : }
83 0 : if( s>=end ) return 0; /* trailing ':' */
84 0 : }
85 :
86 : /* Find the end of this group and check whether it is an embedded
87 : IPv4 address */
88 :
89 0 : char const * tok_end = s;
90 0 : int is_ip4 = 0;
91 0 : while( tok_end<end && *tok_end!=':' ) {
92 0 : is_ip4 |= ( *tok_end=='.' );
93 0 : tok_end++;
94 0 : }
95 :
96 0 : if( is_ip4 ) {
97 0 : uint ip4;
98 0 : if( cnt>6 || tok_end!=end ) return 0; /* must be the last two groups */
99 0 : if( !ip4_parse( s, tok_end, &ip4 ) ) return 0;
100 0 : memcpy( addr+2*cnt, &ip4, 4UL );
101 0 : cnt += 2;
102 0 : s = tok_end;
103 0 : break;
104 0 : }
105 :
106 0 : uint val = 0U;
107 0 : int dig = 0;
108 0 : while( s<end && hex_val( *s )>=0 ) {
109 0 : val = ( val<<4 ) | (uint)hex_val( *s );
110 0 : s++;
111 0 : if( ++dig>4 ) return 0;
112 0 : }
113 0 : if( !dig || cnt>7 ) return 0;
114 0 : addr[ 2*cnt ] = (uchar)( val>>8 );
115 0 : addr[ 2*cnt+1 ] = (uchar)( val );
116 0 : cnt++;
117 0 : expect_sep = 1;
118 :
119 0 : }
120 :
121 0 : if( gap<0 ) {
122 0 : if( cnt!=8 ) return 0;
123 0 : } else {
124 0 : 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 0 : int tail = cnt-gap;
127 0 : if( tail ) memmove( addr+16-2*tail, addr+2*gap, (ulong)( 2*tail ) );
128 0 : memset( addr+2*gap, 0, (ulong)( 16-2*gap-2*tail ) );
129 0 : }
130 :
131 0 : memcpy( out, addr, 16UL );
132 0 : return 1;
133 0 : }
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 0 : char const * end ) {
141 0 : if( s>=end ) return 0U;
142 :
143 0 : int numeric = 1;
144 0 : for( char const * p=s; p<end; p++ ) numeric &= ( *p>='0' && *p<='9' );
145 :
146 0 : if( numeric ) {
147 0 : ulong idx = 0UL;
148 0 : for( char const * p=s; p<end; p++ ) {
149 0 : idx = idx*10UL + (ulong)( *p-'0' );
150 0 : if( idx>UINT_MAX ) return 0U;
151 0 : }
152 0 : return (uint)idx;
153 0 : }
154 :
155 0 : if( (ulong)( end-s )>=IF_NAMESIZE ) return 0U;
156 0 : char name[ IF_NAMESIZE ];
157 0 : memcpy( name, s, (ulong)( end-s ) );
158 0 : name[ end-s ] = '\0';
159 0 : return if_nametoindex( name ); /* 0 if no such interface */
160 0 : }
161 :
162 : int
163 : fd_cstr_to_ip6_addr( char const * s,
164 0 : fd_ip6_addr_t * out ) {
165 :
166 : /* Split off the zone ID, if any */
167 :
168 0 : char const * end = s;
169 0 : char const * zone = NULL;
170 0 : while( *end ) {
171 0 : if( *end=='%' ) { zone = end+1; break; }
172 0 : end++;
173 0 : }
174 :
175 0 : fd_ip6_addr_t addr = {0};
176 0 : if( FD_UNLIKELY( !ip6_parse( s, end, addr.addr ) ) ) return 0;
177 :
178 0 : 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 0 : if( FD_UNLIKELY( !fd_ip6_addr_is_scoped( addr.addr ) ) ) return 0;
183 0 : char const * zone_end = zone;
184 0 : while( *zone_end ) zone_end++;
185 0 : addr.scope_id = zone_parse( zone, zone_end );
186 0 : if( FD_UNLIKELY( !addr.scope_id ) ) return 0;
187 0 : }
188 :
189 0 : *out = addr;
190 0 : return 1;
191 0 : }
192 :
193 : int
194 : fd_cstr_to_ip46_addr( char const * s,
195 0 : fd_ip6_addr_t * out ) {
196 0 : uint ip4_addr;
197 0 : if( fd_cstr_to_ip4_addr( s, &ip4_addr ) ) {
198 0 : fd_ip6_addr_t addr = {0};
199 0 : fd_ip6_addr_ip4_mapped( addr.addr, ip4_addr );
200 0 : *out = addr;
201 0 : return 1;
202 0 : }
203 0 : return fd_cstr_to_ip6_addr( s, out );
204 0 : }
205 :
206 : /* ip4_cstr appends a dotted quad to p */
207 :
208 : static char *
209 : ip4_cstr( char * p,
210 0 : uchar const * octet ) {
211 0 : for( ulong i=0UL; i<4UL; i++ ) {
212 0 : if( i ) p = fd_cstr_append_char( p, '.' );
213 0 : p = fd_cstr_append_printf( p, "%u", (uint)octet[ i ] );
214 0 : }
215 0 : return p;
216 0 : }
217 :
218 : char *
219 : fd_ip6_addr_cstr( char buf[ static FD_IP6_ADDR_CSTR_MAX ],
220 0 : fd_ip6_addr_t const * addr ) {
221 0 : uchar const * a = addr->addr;
222 0 : int ip4 = fd_ip6_addr_is_ip4_mapped( a );
223 :
224 : /* An IPv4 address prints as a dotted quad */
225 :
226 0 : if( ip4 && !addr->scope_id ) {
227 0 : char * p = fd_cstr_init( buf );
228 0 : p = ip4_cstr( p, a+12 );
229 0 : fd_cstr_fini( p );
230 0 : return buf;
231 0 : }
232 :
233 0 : ushort group[ 8 ];
234 0 : 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 0 : int gap = -1, gap_len = 1;
241 0 : for( int i=0; i<8; ) {
242 0 : if( group[ i ] ) { i++; continue; }
243 0 : int j = i;
244 0 : while( j<8 && !group[ j ] ) j++;
245 0 : if( j-i>gap_len ) { gap = i; gap_len = j-i; }
246 0 : i = j;
247 0 : }
248 :
249 0 : char * p = fd_cstr_init( buf );
250 0 : int colon = 0;
251 0 : p = fd_cstr_append_char( p, '[' );
252 0 : for( int i=0; i<8; ) {
253 0 : if( i==gap ) {
254 0 : p = fd_cstr_append_text( p, "::", 2UL );
255 0 : i += gap_len;
256 0 : colon = 0;
257 0 : continue;
258 0 : }
259 0 : if( colon ) p = fd_cstr_append_char( p, ':' );
260 0 : if( ip4 && i==6 ) { /* IPv4-mapped addresses use the mixed notation (RFC 5952 section 5) */
261 0 : p = ip4_cstr( p, a+12 );
262 0 : break;
263 0 : }
264 0 : p = fd_cstr_append_printf( p, "%x", (uint)group[ i ] );
265 0 : colon = 1;
266 0 : i++;
267 0 : }
268 :
269 0 : if( addr->scope_id ) p = fd_cstr_append_printf( p, "%%%u", addr->scope_id );
270 0 : p = fd_cstr_append_char( p, ']' );
271 0 : fd_cstr_fini( p );
272 :
273 0 : return buf;
274 0 : }
|