Line data Source code
1 : #include <sys/socket.h>
2 : #include <netinet/in.h>
3 : #include "fd_netdb.h"
4 : #include <net/if.h>
5 : #include <arpa/inet.h>
6 : #include <ctype.h>
7 : #include <stdlib.h>
8 : #include <string.h>
9 : #include <fcntl.h>
10 : #include <unistd.h>
11 : #include <pthread.h>
12 : #include <errno.h>
13 : #include "fd_resolv.h"
14 : #include "fd_lookup.h"
15 : #include "fd_io_readline.h"
16 : #include "../../util/cstr/fd_cstr.h"
17 : #include "../../util/log/fd_log.h"
18 : #include "../../util/io/fd_io.h"
19 : #include "../../util/net/fd_ip6.h"
20 :
21 : static int
22 3 : is_valid_hostname( char const * host ) {
23 3 : uchar const * s;
24 3 : if( strnlen( host, FD_FQDN_BUF_MAX-1UL )-1UL>=FD_FQDN_BUF_MAX-2UL ) return 0;
25 24 : for( s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || fd_isalnum( *s ); s++ );
26 3 : return !*s;
27 3 : }
28 :
29 : static int
30 : name_from_null( struct address buf[ static 2 ],
31 : char const * name,
32 : int family,
33 0 : int flags ) {
34 0 : int cnt = 0;
35 0 : if( name ) return 0;
36 0 : if( flags & FD_AI_PASSIVE ) {
37 0 : if( family != AF_INET6 )
38 0 : buf[cnt++] = (struct address){ .family = AF_INET };
39 0 : if( family != AF_INET )
40 0 : buf[cnt++] = (struct address){ .family = AF_INET6 };
41 0 : } else {
42 0 : if( family != AF_INET6 )
43 0 : buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } };
44 0 : if( family != AF_INET )
45 0 : buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
46 0 : }
47 0 : return cnt;
48 0 : }
49 :
50 : static int
51 : name_from_numeric( struct address buf[ static 1 ],
52 : char const * name,
53 30 : int family ) {
54 30 : return fd_lookup_ipliteral( buf, name, family );
55 30 : }
56 :
57 : static int
58 : name_from_hosts( struct address buf[ static MAXADDRS ],
59 : char canon[ static FD_FQDN_BUF_MAX ],
60 : char const * name,
61 21 : int family ) {
62 21 : ulong l = strlen( name );
63 21 : int cnt = 0, badfam = 0, have_canon = 0;
64 :
65 21 : if( fd_etc_hosts_fd<0 ) return 0;
66 :
67 21 : if( FD_UNLIKELY( -1==lseek( fd_etc_hosts_fd, 0, SEEK_SET ) ) ) {
68 0 : FD_LOG_ERR(( "lseek(/etc/hosts,0,SEEK_SET) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
69 0 : }
70 :
71 21 : uchar rbuf[1032];
72 21 : fd_io_buffered_istream_t istream[1];
73 21 : fd_io_buffered_istream_init( istream, fd_etc_hosts_fd, rbuf, sizeof(rbuf) );
74 :
75 21 : char line[512];
76 63 : while( cnt < MAXADDRS ) {
77 63 : int err;
78 63 : if( !fd_io_fgets( line, sizeof(line), istream, &err ) ) break;
79 :
80 42 : char *p, *z;
81 :
82 42 : if( (p=strchr( line, '#' )) ) *p++='\n', *p=0;
83 42 : for( p=line+1; (p=strstr( p, name )) &&
84 42 : (!fd_isspace(p[-1]) || !fd_isspace(p[l])); p++ );
85 42 : if( !p ) continue;
86 :
87 : /* Isolate IP address to parse */
88 54 : for( p=line; *p && !fd_isspace(*p); p++ );
89 6 : *p++ = 0;
90 6 : switch( name_from_numeric( buf+cnt, line, family ) ) {
91 6 : case 1:
92 6 : cnt++;
93 6 : break;
94 0 : case 0:
95 0 : continue;
96 0 : default:
97 0 : badfam = FD_EAI_NODATA;
98 0 : break;
99 6 : }
100 :
101 6 : if( have_canon ) continue;
102 :
103 : /* Extract first name as canonical name */
104 3 : for( ; *p && fd_isspace(*p); p++ );
105 24 : for( z=p; *z && !fd_isspace(*z); z++ );
106 3 : *z = 0;
107 3 : if( is_valid_hostname( p ) ) {
108 3 : have_canon = 1;
109 3 : memcpy( canon, p, (ulong)( z-p+1 ) );
110 3 : }
111 3 : }
112 21 : return cnt ? cnt : badfam;
113 21 : }
114 :
115 : struct dpc_ctx {
116 : struct address *addrs;
117 : char *canon;
118 : int cnt;
119 : int rrtype;
120 : };
121 :
122 39 : #define RR_A 1
123 9 : #define RR_CNAME 5
124 0 : #define RR_AAAA 28
125 :
126 12 : #define ABUF_SIZE 4800
127 :
128 : static int
129 : dns_parse_callback( void * c,
130 : int rr,
131 : void const * data,
132 : int len,
133 : void const * packet,
134 9 : int plen ) {
135 9 : char tmp[ FD_FQDN_BUF_MAX ];
136 9 : int family = AF_UNSPEC;
137 9 : struct dpc_ctx *ctx = c;
138 9 : if( rr == RR_CNAME ) {
139 0 : if( fd_dn_expand( packet, (uchar const *)packet + plen,
140 0 : data, tmp, sizeof tmp ) > 0 && is_valid_hostname( tmp ) )
141 0 : strcpy( ctx->canon, tmp );
142 0 : return 0;
143 0 : }
144 9 : if( ctx->cnt >= MAXADDRS ) return 0;
145 9 : if( rr != ctx->rrtype ) return 0;
146 9 : switch( rr ) {
147 9 : case RR_A:
148 9 : if( len != 4 ) return -1;
149 9 : family = AF_INET;
150 9 : break;
151 0 : case RR_AAAA:
152 0 : if( len != 16 ) return -1;
153 0 : family = AF_INET6;
154 0 : break;
155 9 : }
156 9 : ctx->addrs[ctx->cnt].family = family;
157 9 : ctx->addrs[ctx->cnt].scopeid = 0;
158 9 : memcpy( ctx->addrs[ctx->cnt++].addr, data, (ulong)len );
159 9 : return 0;
160 9 : }
161 :
162 : static int
163 : name_from_dns( struct address buf[ static MAXADDRS ],
164 : char canon[ static FD_FQDN_BUF_MAX ],
165 : char const * name,
166 : int family,
167 0 : fd_resolvconf_t const * conf ) {
168 0 : uchar qbuf[2][280], abuf[2][ABUF_SIZE];
169 0 : uchar const * qp[2] = { qbuf[0], qbuf[1] };
170 0 : uchar * ap[2] = { abuf[0], abuf[1] };
171 0 : int qlens[2], alens[2], qtypes[2];
172 0 : int nq = 0;
173 0 : struct dpc_ctx ctx = { .addrs = buf, .canon = canon };
174 0 : static const struct { int af; int rr; } afrr[2] = {
175 0 : { .af = AF_INET6, .rr = RR_A },
176 0 : { .af = AF_INET, .rr = RR_AAAA },
177 0 : };
178 :
179 0 : for( int i=0; i<2; i++ ) {
180 0 : if( family != afrr[i].af ) {
181 0 : qlens[nq] = fd_res_mkquery( 0, name, 1, afrr[i].rr,
182 0 : qbuf[nq], sizeof *qbuf );
183 0 : if( qlens[nq] == -1 )
184 0 : return 0;
185 0 : qtypes[nq] = afrr[i].rr;
186 0 : qbuf[nq][3] = 0; /* don't need AD flag */
187 : /* Ensure query IDs are distinct. */
188 0 : if( nq && qbuf[nq][0] == qbuf[0][0] )
189 0 : qbuf[nq][0]++;
190 0 : nq++;
191 0 : }
192 0 : }
193 :
194 0 : if( fd_res_msend_rc( nq, qp, qlens, ap, alens, sizeof *abuf, conf )<0 )
195 0 : return FD_EAI_SYSTEM-errno;
196 :
197 0 : for( int i=0; i<nq; i++ ) {
198 0 : if( alens[i] < 4 || (abuf[i][3] & 15) == 2 ) return FD_EAI_AGAIN;
199 0 : if( (abuf[i][3] & 15) == 3 ) return 0;
200 0 : if( (abuf[i][3] & 15) != 0 ) return FD_EAI_FAIL;
201 0 : }
202 :
203 0 : for( int i=nq-1; i>=0; i-- ) {
204 0 : ctx.rrtype = qtypes[i];
205 0 : if( alens[i] > (int)sizeof(abuf[i]) ) alens[i] = sizeof abuf[i];
206 0 : fd_dns_parse( abuf[i], alens[i], dns_parse_callback, &ctx );
207 0 : }
208 :
209 0 : if( ctx.cnt ) return ctx.cnt;
210 0 : return FD_EAI_NODATA;
211 0 : }
212 :
213 : static int
214 : name_from_dns_search( struct address buf[ static MAXADDRS ],
215 : char canon[ static FD_FQDN_BUF_MAX ],
216 : char const * name,
217 0 : int family ) {
218 0 : fd_resolvconf_t conf;
219 0 : size_t l;
220 :
221 0 : if( fd_get_resolv_conf( &conf ) < 0 ) return -1;
222 :
223 : /* Count dots, suppress search when >=ndots or name ends in
224 : * a dot, which is an explicit request for global scope. */
225 0 : for( l=0; name[l]; l++ ) {}
226 :
227 : /* Strip final dot for canon, fail if multiple trailing dots. */
228 0 : if( name[l-1]=='.' ) l--;
229 0 : if( !l || name[l-1]=='.' ) return FD_EAI_NONAME;
230 :
231 : /* This can never happen; the caller already checked length. */
232 0 : if( l>=FD_FQDN_BUF_MAX ) return FD_EAI_NONAME;
233 :
234 : /* Name with search domain appended is setup in canon[]. This both
235 : * provides the desired default canonical name (if the requested
236 : * name is not a CNAME record) and serves as a buffer for passing
237 : * the full requested name to name_from_dns. */
238 0 : memcpy( canon, name, l );
239 0 : canon[l] = '.';
240 :
241 0 : canon[l] = 0;
242 0 : return name_from_dns( buf, canon, name, family, &conf );
243 0 : }
244 :
245 : int
246 : fd_dns_ip4_local( char const * name,
247 : uint * addrs,
248 27 : ulong addr_max ) {
249 27 : ulong l = strnlen( name, FD_FQDN_BUF_MAX-1UL );
250 27 : if( FD_UNLIKELY( l-1UL>=FD_FQDN_BUF_MAX-2UL ) ) return FD_EAI_NONAME;
251 :
252 24 : struct address buf[ MAXADDRS ];
253 24 : char canon[ FD_FQDN_BUF_MAX ];
254 24 : int cnt = name_from_numeric( buf, name, AF_INET );
255 24 : if( !cnt ) cnt = name_from_hosts( buf, canon, name, AF_INET );
256 24 : if( cnt<=0 ) return cnt;
257 :
258 6 : ulong out = 0UL;
259 15 : for( int j=0; j<cnt && out<addr_max; j++ ) {
260 9 : if( FD_UNLIKELY( buf[ j ].family!=AF_INET ) ) continue;
261 9 : addrs[ out++ ] = FD_LOAD( uint, buf[ j ].addr );
262 9 : }
263 6 : return out ? (int)out : FD_EAI_NODATA;
264 24 : }
265 :
266 : int
267 : fd_dns_ip4_query( char const * name,
268 18 : uchar query[ static FD_DNS_QUERY_MTU ] ) {
269 18 : int qlen = fd_res_mkquery( 0, name, 1, RR_A, query, FD_DNS_QUERY_MTU );
270 18 : if( FD_UNLIKELY( qlen==-1 ) ) return FD_EAI_NONAME;
271 18 : query[ 3 ] = 0; /* don't need AD flag */
272 18 : return qlen;
273 18 : }
274 :
275 : int
276 : fd_dns_ip4_answer( uchar const * answer,
277 : ulong answer_sz,
278 : uint * addrs,
279 18 : ulong addr_max ) {
280 18 : if( FD_UNLIKELY( answer_sz<12UL ) ) return FD_EAI_AGAIN; /* short of a full header */
281 18 : if( FD_UNLIKELY( (answer[ 3 ]&15)==2 ) ) return FD_EAI_AGAIN; /* SERVFAIL */
282 15 : if( FD_UNLIKELY( (answer[ 3 ]&15)==3 ) ) return FD_EAI_NONAME; /* NXDOMAIN */
283 12 : if( FD_UNLIKELY( (answer[ 3 ]&15)!=0 ) ) return FD_EAI_FAIL;
284 :
285 12 : struct address buf[ MAXADDRS ];
286 12 : char canon[ FD_FQDN_BUF_MAX ] = {0};
287 12 : struct dpc_ctx ctx = { .addrs = buf, .canon = canon, .rrtype = RR_A };
288 12 : int parse_err = fd_dns_parse( answer, (int)fd_ulong_min( answer_sz, ABUF_SIZE ), dns_parse_callback, &ctx );
289 : /* A malformed/truncated packet with no usable leading records is
290 : retryable, not proof of no data. Records parsed before the
291 : malformation are still good (truncated replies). */
292 12 : if( FD_UNLIKELY( parse_err<0 && !ctx.cnt ) ) return FD_EAI_AGAIN;
293 9 : if( FD_UNLIKELY( !ctx.cnt ) ) return FD_EAI_NODATA;
294 :
295 6 : ulong out = 0UL;
296 15 : for( int j=0; j<ctx.cnt && out<addr_max; j++ ) {
297 9 : if( FD_UNLIKELY( buf[ j ].family!=AF_INET ) ) continue;
298 9 : addrs[ out++ ] = FD_LOAD( uint, buf[ j ].addr );
299 9 : }
300 6 : return out ? (int)out : FD_EAI_NODATA;
301 9 : }
302 :
303 : int
304 : fd_dns_nameservers( uint * addrs,
305 : ulong addr_max,
306 : uint * opt_timeout,
307 12 : uint * opt_attempts ) {
308 12 : fd_resolvconf_t conf;
309 12 : if( FD_UNLIKELY( fd_get_resolv_conf( &conf )<0 ) ) return FD_EAI_SYSTEM;
310 12 : if( FD_LIKELY( opt_timeout ) ) *opt_timeout = conf.timeout;
311 12 : if( FD_LIKELY( opt_attempts ) ) *opt_attempts = conf.attempts;
312 :
313 12 : ulong out = 0UL;
314 24 : for( uint i=0U; i<conf.nns && out<addr_max; i++ ) {
315 12 : if( FD_UNLIKELY( conf.ns[ i ].family!=AF_INET ) ) continue; /* v6 nameservers unsupported */
316 12 : addrs[ out++ ] = FD_LOAD( uint, conf.ns[ i ].addr );
317 12 : }
318 : /* v6-only resolv.conf: same 127.0.0.1 fallback as no nameservers */
319 12 : if( FD_UNLIKELY( !out && addr_max ) ) {
320 0 : if( FD_UNLIKELY( conf.nns ) ) FD_LOG_WARNING(( "IPv6 nameservers in /etc/resolv.conf are unsupported; trying 127.0.0.1" ));
321 0 : addrs[ out++ ] = fd_uint_bswap( INADDR_LOOPBACK );
322 0 : }
323 12 : return (int)out;
324 12 : }
325 :
326 : static const struct policy {
327 : uchar addr[16];
328 : uchar len, mask;
329 : uchar prec, label;
330 : } defpolicy[] = {
331 : { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01},
332 : 15, 0xff, 50, 0 },
333 : { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff},
334 : 11, 0xff, 35, 4 },
335 : { {0x20,0x02}, 1, 0xff, 30, 2 },
336 : { {0x20,0x01}, 3, 0xff, 5, 5 },
337 : { {0xfc}, 0, 0xfe, 3, 13 },
338 : #if 0
339 : /* These are deprecated and/or returned to the address
340 : * pool, so despite the RFC, treating them as special
341 : * is probably wrong. */
342 : { "", 11, 0xff, 1, 3 },
343 : { "\xfe\xc0", 1, 0xc0, 1, 11 },
344 : { "\x3f\xfe", 1, 0xff, 1, 12 },
345 : #endif
346 : /* Last rule must match all addresses to stop loop. */
347 : { "", 0, 0, 40, 1 },
348 : };
349 :
350 : static const struct policy *
351 0 : policyof( struct in6_addr const * a ) {
352 0 : for( int i=0; ; i++ ) {
353 0 : if( memcmp( a->s6_addr, defpolicy[i].addr, defpolicy[i].len ) )
354 0 : continue;
355 0 : if( (a->s6_addr[defpolicy[i].len] & defpolicy[i].mask)
356 0 : != defpolicy[i].addr[defpolicy[i].len] )
357 0 : continue;
358 0 : return defpolicy+i;
359 0 : }
360 0 : }
361 :
362 : static int
363 0 : labelof( struct in6_addr const * a ) {
364 0 : return policyof( a )->label;
365 0 : }
366 :
367 : static int
368 0 : scopeof( struct in6_addr const * a ) {
369 0 : if( IN6_IS_ADDR_MULTICAST(a) ) return a->s6_addr[1] & 15;
370 0 : if( IN6_IS_ADDR_LINKLOCAL(a) ) return 2;
371 0 : if( IN6_IS_ADDR_LOOPBACK (a) ) return 2;
372 0 : if( IN6_IS_ADDR_SITELOCAL(a) ) return 5;
373 0 : return 14;
374 0 : }
375 :
376 : static int
377 : prefixmatch( struct in6_addr const * s,
378 0 : struct in6_addr const * d ) {
379 : /* FIXME: The common prefix length should be limited to no greater
380 : * than the nominal length of the prefix portion of the source
381 : * address. However the definition of the source prefix length is
382 : * not clear and thus this limiting is not yet implemented. */
383 0 : uint i;
384 0 : for( i=0; i<128 && !((s->s6_addr[i/8]^d->s6_addr[i/8])&(128>>(i%8))); i++ );
385 0 : return (int)i;
386 0 : }
387 :
388 0 : #define DAS_USABLE 0x40000000
389 0 : #define DAS_MATCHINGSCOPE 0x20000000
390 0 : #define DAS_MATCHINGLABEL 0x10000000
391 0 : #define DAS_PREC_SHIFT 20
392 0 : #define DAS_SCOPE_SHIFT 16
393 0 : #define DAS_PREFIX_SHIFT 8
394 0 : #define DAS_ORDER_SHIFT 0
395 :
396 : static int
397 : addrcmp( void const * _a,
398 0 : void const * _b ) {
399 0 : struct address const * a = _a, *b = _b;
400 0 : return b->sortkey - a->sortkey;
401 0 : }
402 :
403 : int
404 : fd_lookup_name( struct address buf[ static MAXADDRS ],
405 : char canon[ static FD_FQDN_BUF_MAX ],
406 : char const * name,
407 : int family,
408 0 : int flags ) {
409 0 : int cnt = 0, i, j;
410 :
411 0 : *canon = 0;
412 0 : if( name ) {
413 : /* reject empty name and check len so it fits into temp bufs */
414 0 : size_t l = strnlen( name, FD_FQDN_BUF_MAX-1UL );
415 0 : if( l-1UL>=FD_FQDN_BUF_MAX-2UL )
416 0 : return FD_EAI_NONAME;
417 0 : memcpy( canon, name, l+1 );
418 0 : }
419 :
420 : /* Procedurally, a request for v6 addresses with the v4-mapped
421 : * flag set is like a request for unspecified family, followed
422 : * by filtering of the results. */
423 0 : if( flags & FD_AI_V4MAPPED ) {
424 0 : if( family == AF_INET6 ) family = AF_UNSPEC;
425 0 : else flags -= FD_AI_V4MAPPED;
426 0 : }
427 :
428 : /* Try each backend until there's at least one result. */
429 0 : cnt = name_from_null( buf, name, family, flags );
430 0 : if( !cnt ) cnt = name_from_numeric( buf, name, family );
431 0 : if( !cnt && !(flags & FD_AI_NUMERICHOST) ) {
432 0 : cnt = name_from_hosts( buf, canon, name, family );
433 0 : if( !cnt ) cnt = name_from_dns_search( buf, canon, name, family );
434 0 : }
435 0 : if( cnt<=0 ) return cnt ? cnt : FD_EAI_NONAME;
436 :
437 : /* Filter/transform results for v4-mapped lookup, if requested. */
438 0 : if( flags & FD_AI_V4MAPPED ) {
439 0 : if( !(flags & FD_AI_ALL) ) {
440 : /* If any v6 results exist, remove v4 results. */
441 0 : for( i=0; i<cnt && buf[i].family != AF_INET6; i++ );
442 0 : if( i<cnt ) {
443 0 : for( j=0; i<cnt; i++ ) {
444 0 : if( buf[i].family == AF_INET6 )
445 0 : buf[j++] = buf[i];
446 0 : }
447 0 : cnt = i = j;
448 0 : }
449 0 : }
450 : /* Translate any remaining v4 results to v6 */
451 0 : for( int i=0; i<cnt; i++ ) {
452 0 : if( buf[i].family != AF_INET ) continue;
453 0 : uint ip4_addr = FD_LOAD( uint, buf[i].addr );
454 0 : fd_ip6_addr_ip4_mapped( buf[i].addr, ip4_addr );
455 0 : buf[i].family = AF_INET6;
456 0 : }
457 0 : }
458 :
459 : /* No further processing is needed if there are fewer than 2
460 : * results or if there are only IPv4 results. */
461 0 : if( cnt<2 || family==AF_INET ) return cnt;
462 0 : for( i=0; i<cnt; i++ ) if( buf[i].family != AF_INET ) break;
463 0 : if( i==cnt ) return cnt;
464 :
465 : /* The following implements a subset of RFC 3484/6724 destination
466 : * address selection by generating a single 31-bit sort key for
467 : * each address. Rules 3, 4, and 7 are omitted for having
468 : * excessive runtime and code size cost and dubious benefit.
469 : * So far the label/precedence table cannot be customized. */
470 0 : for( int i=0; i<cnt; i++ ) {
471 0 : int family = buf[i].family;
472 0 : int key = 0;
473 0 : struct sockaddr_in6 sa6 = { 0 }, da6 = {
474 0 : .sin6_family = AF_INET6,
475 0 : .sin6_scope_id = buf[i].scopeid,
476 0 : .sin6_port = 65535
477 0 : };
478 0 : struct sockaddr_in sa4 = { 0 }, da4 = {
479 0 : .sin_family = AF_INET,
480 0 : .sin_port = 65535
481 0 : };
482 0 : void *sa, *da;
483 0 : socklen_t salen, dalen;
484 0 : if( family == AF_INET6 ) {
485 0 : memcpy( da6.sin6_addr.s6_addr, buf[i].addr, 16 );
486 0 : da = &da6; dalen = sizeof da6;
487 0 : sa = &sa6; salen = sizeof sa6;
488 0 : } else {
489 0 : uint ip4_addr = FD_LOAD( uint, buf[i].addr );
490 0 : fd_ip6_addr_ip4_mapped( sa6.sin6_addr.s6_addr, ip4_addr );
491 0 : fd_ip6_addr_ip4_mapped( da6.sin6_addr.s6_addr, ip4_addr );
492 0 : memcpy( &da4.sin_addr, buf[i].addr, 4 );
493 0 : da = &da4; dalen = sizeof da4;
494 0 : sa = &sa4; salen = sizeof sa4;
495 0 : }
496 0 : const struct policy *dpolicy = policyof( &da6.sin6_addr );
497 0 : int dscope = scopeof( &da6.sin6_addr );
498 0 : int dlabel = dpolicy->label;
499 0 : int dprec = dpolicy->prec;
500 0 : int prefixlen = 0;
501 0 : int fd = socket( family, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP );
502 0 : if( fd >= 0 ) {
503 0 : if( !connect( fd, da, dalen ) ) {
504 0 : key |= DAS_USABLE;
505 0 : if( !getsockname( fd, sa, &salen ) ) {
506 0 : if( family == AF_INET ) memcpy(
507 0 : sa6.sin6_addr.s6_addr+12,
508 0 : &sa4.sin_addr, 4);
509 0 : if( dscope == scopeof( &sa6.sin6_addr ) )
510 0 : key |= DAS_MATCHINGSCOPE;
511 0 : if( dlabel == labelof( &sa6.sin6_addr ) )
512 0 : key |= DAS_MATCHINGLABEL;
513 0 : prefixlen = prefixmatch( &sa6.sin6_addr, &da6.sin6_addr );
514 0 : }
515 0 : }
516 0 : close( fd );
517 0 : }
518 0 : key |= dprec << DAS_PREC_SHIFT;
519 0 : key |= (15-dscope) << DAS_SCOPE_SHIFT;
520 0 : key |= prefixlen << DAS_PREFIX_SHIFT;
521 0 : key |= (MAXADDRS-i) << DAS_ORDER_SHIFT;
522 0 : buf[i].sortkey = key;
523 0 : }
524 0 : qsort( buf, (ulong)cnt, sizeof *buf, addrcmp );
525 :
526 0 : return cnt;
527 0 : }
|